Last active
August 22, 2016 21:08
-
-
Save alexeiz/df33d3c8702eab4765a1d7367e29a11b to your computer and use it in GitHub Desktop.
stones game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from pprint import pprint | |
from copy import deepcopy | |
import functools | |
import sys | |
import re | |
def hash_game(game): | |
return game.get_hash() | |
def memoize(obj): | |
cache = obj.cache = {} | |
@functools.wraps(obj) | |
def memoizer(*args, **kwargs): | |
key = hash_game(*args) | |
if key not in cache: | |
cache[key] = obj(*args, **kwargs) | |
return cache[key] | |
return memoizer | |
def minimax(game_state): | |
return max( | |
map(lambda move: (move, min_play(game_state.next_state(move))), | |
game_state.get_available_moves()), | |
key = lambda x: x[1]) | |
@memoize | |
def min_play(game_state): | |
if game_state.is_gameover(): | |
return game_state.evaluate() | |
return min( | |
map(lambda move: max_play(game_state.next_state(move)), | |
game_state.get_available_moves())) | |
@memoize | |
def max_play(game_state): | |
if game_state.is_gameover(): | |
return game_state.evaluate() | |
return max( | |
map(lambda move: min_play(game_state.next_state(move)), | |
game_state.get_available_moves())) | |
class Stones(object): | |
def __init__(self, m, n): | |
self.m = m | |
self.n = n | |
self.stones = m * n | |
self.board = [[1 for y in range(n)] for x in range(m)] | |
self.side = 0 # white | |
self.moves = [] | |
pass | |
def get_available_moves(self): | |
moves = [] | |
for i,row in enumerate(self.board): | |
for j,cell in enumerate(row): | |
if cell: | |
moves.append((i, j)) | |
return moves | |
def next_state(self, move): | |
succ = deepcopy(self) | |
succ.moves.append(move) | |
succ.side = 1 - self.side | |
for i in range(move[0], succ.m): | |
if succ.board[i][move[1]] == 0: | |
break | |
else: | |
for j in range(move[1], succ.n): | |
if succ.board[i][j] == 0: | |
break | |
else: | |
succ.board[i][j] = 0 | |
succ.stones -= 1 | |
return succ | |
def is_gameover(self): | |
return self.stones == 0 | |
def evaluate(self): | |
return 1 if self.side == 0 else -1 | |
def get_hash(self): | |
return str((self.board, self.side)) | |
def display(self): | |
for j in range(self.n): | |
print("{:2} ".format(self.n - j), end="") | |
for i in range(self.m): | |
if self.board[i][self.n - j - 1]: | |
print('* ', end="") | |
else: | |
print(' ', end="") | |
print() | |
print(" ", end="") | |
for i in range(self.m): | |
print("{:2}".format(i + 1), end="") | |
print() | |
def ask_move(): | |
move = input("Your move? ") | |
m = re.match(r"(\d+),\s*(\d+)", move) | |
if m: | |
return (int(m.group(1)) - 1, int(m.group(2)) - 1) | |
def main(): | |
game = Stones(int(sys.argv[1]), int(sys.argv[2])) | |
while not game.is_gameover(): | |
move = minimax(game) | |
print("{}, {} : {}".format(move[0][0] + 1, move[0][1] + 1, move[1])) | |
game = game.next_state(move[0]) | |
game.display() | |
move = ask_move() | |
game = game.next_state(move) | |
game.display() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment