A Nim game with a twist: you can reject your opponent's move.

You and AI take turns moving. Each person can take away any number of stones from one of the piles. Whoever takes the last stone wins. The catch is that each turn when a move is made, that move can be rejected by the other person, as long as there is another move. Rejection can only be used once per turn. There are 3 levels in total. In each level, you have a winning strategy, but it's up to you to find it. Level 1 is not that difficult, since it has only a single pile of stones.

StatusReleased
PlatformsHTML5
Rating
Rated 4.5 out of 5 stars
(2 total ratings)
Authormatrix67
GenrePuzzle
TagsBoard Game, Math
Average sessionA few minutes
LanguagesEnglish
InputsMouse, Touchscreen

Comments

Log in with itch.io to leave a comment.

(1 edit)

Here's my code too. It lists only winning positions on the left and the two positions to the right are the moves you should make in case one gets rejected.

from functools import cache
@cache
def is_winnable(pos):
    if sum(pos) == 0:
        return False
    elif sum(pos) == 1:
        return True
    lose_psns = []
    for i in range(len(pos)):
        for dots in range(pos[i]):
            child_pos = tuple(sorted(pos[:i] + (dots,) + pos[i+1:], reverse=True))
            if not is_winnable(child_pos):
                lose_psns.append(child_pos)
                if len(lose_psns) >= 2:
                    print(pos, lose_psns)
                    return True
    return False
for a in range(6):
    for b in range(6):
        for c in range(6):
            is_winnable(tuple(sorted((a, b, c), reverse=True)))
(+2)(-1)

#Here's my code to calculate all must-win or must-lose 

#forms in 3d.

set_lose={(0,0,0)}

set_win={(0,1,0),(1,0,0),(0,0,1)}

limit=5

def add_win(i,j,k):

    set_win.add((i,j,k))

    set_win.add((i,k,j))

    set_win.add((k,i,j))

    set_win.add((k,j,i))

    set_win.add((j,i,k))

    set_win.add((j,k,i))

def have_common(a,b):

    if a[0]==b[0]:

        if a[1]==b[1]:

            for i in range(max(a[2],b[2])+1,limit+1):

                add_win(a[0],a[1],i)

        elif a[2]==b[2]:

            for i in range(max(a[1],b[1])+1,limit+1):

                add_win(a[0],i,a[2])

        else:

            if (a[1]>b[1]) and (a[2]<b[2]):

                add_win(a[0],a[1],b[2])

            if(a[1]<b[1]) and (a[2]>b[2]):

                add_win(a[0],b[1],a[2])

    elif a[1]==b[1]:

        if a[2]==b[2]:

            for i in range(max(a[0],b[0])+1,limit+1):

                add_win(i,a[1],a[2])

        else:

            if(a[0]>b[0]) and (a[2]<b[2]):

                add_win(a[0],a[1],b[2])

            if(a[0]<b[0]) and(a[2]>b[2]):

                add_win(b[0],a[1],a[2])

    elif a[2]==b[2]:

        if(a[0]>b[0]) and (a[1]<b[1]):

            add_win(a[0],b[1],a[2])

        if(a[0]<b[0]) and (a[1]>b[1]):

            add_win(b[0],a[1],a[2])

def xinzeng(dot):

    for i in set_lose:

        have_common(i,dot)

for i in range(0,limit+1):

    for j in range(0,limit+1):

        for k in range(0,limit+1):

            dot=(i,j,k)

            if (dot not in set_win)and(dot not in set_lose):

                # print(i,j,k)

                xinzeng(dot)

                set_lose.add(dot)

print(len(set_lose))

for i in set_lose:

    print(i)