• (Mastermind) puzzle (with 3 digits) -- Elegant (readable) code Sought

    From HenHanna@HenHanna@gmail.com to comp.lang.python on Sun Feb 25 13:04:34 2024
    From Newsgroup: comp.lang.python


    (i just wrote (non-elegant) Python code.)


    Could you share a short, VERY Readable Pythonic code that solves this?

    Thank you!


    https://i.imgur.com/72LGJjj.jpeg

    3 digit lock
    [682]: One number is correct and well-placed
    [614]: One number is correct but wrongly placed
    [206]: Two numbers are correct but wrongly placed
    [738]: Nothing is correct
    [780]: One number is correct but wrongly placed


    HINT -- A mark of a great puzzle, this one contains a surprises or two.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.python on Sun Feb 25 23:45:02 2024
    From Newsgroup: comp.lang.python

    On Sun, 25 Feb 2024 13:04:34 -0800, HenHanna wrote:

    Could you share a short, VERY Readable Pythonic code that solves this?

    import itertools

    def score(candidate, answer) :
    return \
    (
    sum(a == b for a, b in zip(candidate, answer)),
    sum
    (
    i != j and a == b
    for i, a in enumerate(candidate)
    for j, b in enumerate(answer)
    )
    )
    #end score

    required_scores = \
    (
    ((6, 8, 2), (1, 0)),
    ((6, 1, 4), (0, 1)),
    ((2, 0, 6), (0, 2)),
    ((7, 3, 8), (0, 0)),
    ((7, 8, 0), (0, 1)),
    )

    for answer in itertools.product(*(range(10),) * 3) :
    if all \
    (
    score(candidate, answer) == required_score
    for candidate, required_score in required_scores
    ) \
    :
    print(answer)
    #end if
    #end for
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From HenHanna@HenHanna@gmail.com (HenHanna) to comp.lang.python on Mon Feb 26 03:46:48 2024
    From Newsgroup: comp.lang.python

    wow... Thank you... I'm glad i asked.



    HINT -- A mark of a great puzzle, this one contains a surprise

    == the last clue is not required.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.python on Mon Feb 26 00:03:11 2024
    From Newsgroup: comp.lang.python

    HenHanna <HenHanna@gmail.com> writes:
    Could you share a short, VERY Readable Pythonic code that solves this?

    Here is my brute force solution. It prints the list [42] which means
    that the 3-digit answer is 042.

    def check(n: int) -> bool:
    """return true iff n satisfies all the constraints. n is a 3 digit number
    possibly with leading zeros."""
    a,b,c = n//100, (n//10)%10, n%10

    # 682 -- one number correct and well placed
    k1 = (a==6 or b==8 or c==2) and len({a,b,c} & {6,8,2})==1
    # 614 -- one number correct but wrongly placed
    k2 = a != 6 and b != 1 and c != 4 and \
    len({a,b,c} & {6,1,4})==1
    # 206 -- two numbers correct but wrongly placed
    k3 = len({a,b,c} & {2,0,6})==2 and a!=2 and b!=0 and c!=6
    # 738 -- nothing correct
    k4 = len({a,b,c} & {7,3,8}) == 0
    # 780 -- one number correct but wrongly placed
    k5 = len({a,b,c} & {7,8,0})==1 and a!=7 and b!=8 and c!=1

    return all([k1,k2,k3,k4,k5])

    print(list(filter(check, range(0,1000))))
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.python on Mon Feb 26 00:35:37 2024
    From Newsgroup: comp.lang.python

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    Could you share a short, VERY Readable Pythonic code that solves this?
    def score(candidate, answer) :

    I like that approach. Here is my version:

    from typing import Tuple
    def digits(n): return n//100, (n//10)%10, n%10

    def score(answer,n) -> Tuple[int,int]:
    def count(*v): return sum(filter(bool,v))
    x,y,z = digits(answer)
    a,b,c = digits(n)

    well_placed = count(a==x,b==y,c==z)
    wrongly_placed = count(a in {y,z}, b in {x,z}, c in {x,y})
    return well_placed, wrongly_placed

    wanted = [(682,1,0),(614,0,1),(206,0,2),(738,0,0),(780,0,1)]

    def check(n: int) -> bool:
    return all(score(n,a)==(b,c) for a,b,c in wanted)

    print(list(filter(check2, range(0,1000))))
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.python on Mon Feb 26 00:48:40 2024
    From Newsgroup: comp.lang.python

    Paul Rubin <no.email@nospam.invalid> writes:
    I like that approach. Here is my version:

    Oops, garbled. Fixed here, and I updated to follow your naming
    convention.
    ================================================================
    from typing import Tuple
    def digits(n): return n//100, (n//10)%10, n%10

    def score(answer,candidate) -> Tuple[int,int]:
    def count(*v): return sum(filter(bool,v))
    x,y,z = digits(answer)
    a,b,c = digits(candidate)

    well_placed = count(a==x,b==y,c==z)
    wrongly_placed = count(a in {y,z}, b in {x,z}, c in {x,y})
    return well_placed, wrongly_placed

    wanted = [(682,1,0),(614,0,1),(206,0,2),(738,0,0),(780,0,1)]

    def check2(n: int) -> bool:
    return all(score(n,candidate)==(right,wrong)
    for candidate,right,wrong in wanted)

    print(list(filter(check2, range(0,1000))))
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From HenHanna@HenHanna@gmail.com (HenHanna) to comp.lang.python on Mon Feb 26 09:55:09 2024
    From Newsgroup: comp.lang.python

    wow... Thank you... I'm glad i asked.


    HINT -- A mark of a great puzzle, this one contains a surprise (or two)
    == the last 2 clues are not required.



    1. Could there be a different approach? (not exhaustive search) ?


    2. What's a nice tweak on this problem that
    would call for a program that's just a bit longer, harder?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.python on Mon Feb 26 08:54:39 2024
    From Newsgroup: comp.lang.python

    HenHanna@gmail.com (HenHanna) writes:
    1. Could there be a different approach? (not exhaustive search) ?

    Yes, this type of puzzle is called a constraint satisfaction problem
    (CSP). There is a big literature on how to solve those. Basically you
    use the different clues to narrow the search space. There is a language
    called Prolog which is designed for this type of problem. Beyond that,
    there are tools called SAT solvers (SAT = boolean satisfiability
    problem) that try to solve a related class of problems as efficiently as
    they can. But, in general, there is no known algorithm that solves the
    entire class efficiently, and probably none exists (this is the famous P
    vs NP problem).

    2. What's a nice tweak on this problem that
    would call for a program that's just a bit longer, harder?

    I think it is ok the way it is.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From HenHanna@HenHanna@gmail.com (HenHanna) to comp.lang.python on Tue Feb 27 18:51:38 2024
    From Newsgroup: comp.lang.python

    bard.google.com (now Gemini) can't write Python code that solves this.

    can ChatGPT do it?

    Paul Rubin wrote:
    (HenHanna) writes:
    1. Could there be a different approach? (not exhaustive search) ?

    Yes, this type of puzzle is called a constraint satisfaction problem
    (CSP). There is a big literature on how to solve those. Basically you
    use the different clues to narrow the search space. There is a language called Prolog which is designed for this type of problem.


    i have Knuth's book on CSP... i'll check inside.


    yes, i studied Prolog when i was younger. (Wrote a Prolog interpreter in Lisp, etc.)

    Is this a typical easy exercise in Prolog programming? Could someone share Prolog code for it?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Greg Ewing@greg.ewing@canterbury.ac.nz to comp.lang.python on Wed Feb 28 17:29:54 2024
    From Newsgroup: comp.lang.python

    On 26/02/24 12:45 pm, Lawrence D'Oliveiro wrote:
    def score(candidate, answer) :
    return \
    (
    sum(a == b for a, b in zip(candidate, answer)),
    sum
    (
    i != j and a == b
    for i, a in enumerate(candidate)
    for j, b in enumerate(answer)
    )
    )

    This is not correct. score((1,1,1), (1,1,2)) gives (2,4). According to
    the usual rules of Mastermind, it should be (2, 0).
    --
    Greg

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.python on Thu Mar 14 05:53:53 2024
    From Newsgroup: comp.lang.python

    On Wed, 28 Feb 2024 17:29:54 +1300, Greg Ewing wrote:

    This is not correct. score((1,1,1), (1,1,2)) gives (2,4). According to
    the usual rules of Mastermind, it should be (2, 0).

    How about this as a more general Mastermind scoring function, then:

    def score(candidate, answer) :
    return \
    (
    sum(a == b for a, b in zip(candidate, answer)),
    sum
    (
    i != j and a == b
    for i, a in enumerate(candidate)
    for j, b in enumerate(answer)
    for s in (set(i for i, (a, b) in enumerate(zip(candidate, answer)) if a == b),)
    if i not in s and j not in s
    )
    )
    #end score
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.python on Thu Mar 14 19:19:16 2024
    From Newsgroup: comp.lang.python

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    How about this as a more general Mastermind scoring function, then:

    This works for me:

    from collections import Counter as multiset
    def msum(m: multiset) -> int: return sum(m.values())
    def digits(n: int) -> int: return n//100, (n//10)%10, n%10

    def score(answer: int,candidate: int) -> Tuple[int,int]:
    a = digits(answer)
    b = digits(candidate)
    well_placed = sum(x==y for x,y in zip(a,b))
    wrongly_placed = msum(multiset(a) & multiset(b)) - well_placed
    return well_placed, wrongly_placed

    print (score(111,112)) # prints (2, 0)
    --- Synchronet 3.20a-Linux NewsLink 1.114