Skip to content

Instantly share code, notes, and snippets.

@fbparis
Last active April 16, 2020 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fbparis/c05c3d0a5ffcc0b0374c090e958f6561 to your computer and use it in GitHub Desktop.
Save fbparis/c05c3d0a5ffcc0b0374c090e958f6561 to your computer and use it in GitHub Desktop.
Simulate niya games with alternative rules
from itertools import product, combinations
from random import sample
# 0 1 2 3
# 4 5 6 7
# 8 9 A B
# C D E F
cards = list(product('ABCD', '0123', repeat=1))
border = [0, 1, 2, 3, 7, 11, 15, 14, 13, 12, 8, 4]
figures = {'0123', '4567', '89AB','CDEF', '048C', '159D', '26AE', '37BF', '05AF', '369C', '0145', '1256', '2367', '4589', '569A', '67AB', '89CD', '9ADE', 'ABEF'}
def niya(board, possibleMoves, myFigures, opponentFigures, depth=0):
playerId = depth % 2
if len(possibleMoves) == 0:
return None, -1
if depth >= 6: # need at least 4 tokens to win
for moveIndex in possibleMoves:
coord = '%X' % moveIndex
for figure in myFigures:
if coord in figure:
win = True
for c in figure:
if c == coord:
continue
if board[int(c, 16)] != ('P', playerId):
win = False
break
if win:
return moveIndex, 1
if depth == 15: # draw
return moveIndex, 0
bestScore = 2
for moveIndex in possibleMoves:
coord = '%X' % moveIndex
move, score = niya([board[i] if i != moveIndex else ('P', playerId) for i in range(16)], [i for i in range(16) if i != moveIndex and (board[i][0] == board[moveIndex][0] or board[i][1] == board[moveIndex][1])], {x for x in opponentFigures if coord not in x}, myFigures, depth + 1)
if score < bestScore:
bestMove, bestScore = moveIndex, score
if score == -1:
break
return bestMove, -bestScore
def best_move(board, possibleMoves, myFigures, opponentFigures, depth=0):
playerId = depth % 2
if len(possibleMoves) == 0:
return None, -1
if depth == 1: # last player plays twice on first turn
bestScore = 2
for move1, move2 in combinations(possibleMoves, 2):
if board[move1][0] != board[move2][0] and board[move1][1] != board[move2][1]: # only valid moves
continue
coord1, coord2 = '%X' % move1, '%X' % move2
move, score = best_move([board[i] if i not in (move1, move2) else ('P', playerId) for i in range(16)], [i for i in range(16) if i not in (move1, move2) and (board[i][0] == board[move2][0] or board[i][1] == board[move2][1])], {x for x in opponentFigures if coord1 not in x and coord2 not in x}, myFigures, depth + 1)
if score < bestScore:
bestMove, bestScore = (move1, move2), score
if score == -1:
break
return bestMove, -bestScore
elif depth >= 5: # need at least 4 tokens to win
for moveIndex in possibleMoves:
coord = '%X' % moveIndex
for figure in myFigures:
if coord in figure:
win = True
for c in figure:
if c == coord:
continue
if board[int(c, 16)] != ('P', playerId):
win = False
break
if win:
return moveIndex, 1
if depth == 14: # draw
return moveIndex, 0
bestScore = 2
for moveIndex in possibleMoves:
coord = '%X' % moveIndex
move, score = best_move([board[i] if i != moveIndex else ('P', playerId) for i in range(16)], [i for i in range(16) if i != moveIndex and (board[i][0] == board[moveIndex][0] or board[i][1] == board[moveIndex][1])], {x for x in opponentFigures if coord not in x}, myFigures, depth + 1)
if score < bestScore:
bestMove, bestScore = moveIndex, score
if score == -1:
break
return bestMove, -bestScore
if __name__ == '__main__':
N = 100
win, draw = [], []
for i in range(N):
move, score = niya(sample(cards, 16), border, figures.copy(), figures.copy()) # Niya rules
# move, score = best_move(sample(cards, 16), [5, 6, 10, 9, 0, 3, 15, 12, 1, 2, 7, 11, 14, 13, 8, 4], figures.copy(), figures.copy()) # Alternative rules
print('%d: %X, %d' % (i, move, score))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment