Created
September 13, 2021 12:05
-
-
Save alekxeyuk/65da0e9a032c6a4b0250eee931375a75 to your computer and use it in GitHub Desktop.
Domino game with basic AI
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
from enum import Enum, auto | |
from itertools import cycle | |
from typing import Counter, List, Tuple | |
import random | |
class GameState(Enum): | |
INIT = auto() | |
RUN = auto() | |
WIN = auto() | |
LOSS = auto() | |
DRAW = auto() | |
class Illegal(Exception): | |
pass | |
class DominoGame: | |
def __init__(self) -> None: | |
self.state = GameState.INIT | |
self.field: List[List[int]] = list() | |
self.status: str = str() | |
self.init() | |
def init(self) -> None: | |
pieces = [[x, y] for y in range(7) for x in range(y + 1)] | |
self.doubles = [[i, i] for i in range(7)] | |
random.shuffle(pieces) | |
self.stock = pieces[:14] | |
self.computer = pieces[14::2] | |
self.player = pieces[15::2] | |
self.data_base = {'c': self.computer, 'p': self.player} | |
winner = '' | |
for d in self.doubles: | |
if d in self.computer: | |
winner = ('c', d) | |
self.status = 'It\'s your turn to make a move. Enter your command.' | |
self.order = cycle('pc') | |
elif d in self.player: | |
winner = ('p', d) | |
self.status = 'Computer is about to make a move. Press Enter to continue...' | |
self.order = cycle('cp') | |
if winner: | |
self.data_base[winner[0]].remove(winner[1]) | |
self.field.append(winner[1]) | |
def print_field(self) -> None: | |
if len(self.field) > 6: | |
print("".join(map(str, self.field[:3])), "".join(map(str, self.field[-3:])), sep="...") | |
else: | |
print("".join(map(str, self.field))) | |
def print(self) -> None: | |
print('=' * 70) | |
print('Stock size::', len(self.stock)) | |
print('Computer pieces:', len(self.computer), '\n') | |
self.print_field() | |
print() | |
print('Your pieces:') | |
for i, domino in enumerate(self.player, 1): | |
print(i, domino, sep=':') | |
print() | |
print('Status:', self.status) | |
def is_draw(self) -> bool: | |
if self.field[0][0] == self.field[-1][-1]: | |
if str(self.field).count(str(self.field[0][0])) == 8: | |
return True | |
return False | |
def is_legal(self, end: List[int], domino: List[int], side: int) -> bool: | |
if side == -1: | |
end_v = end[0] | |
if domino[1] == end_v: | |
return True | |
domino.reverse() | |
if domino[1] == end_v: | |
return True | |
return False | |
else: | |
end_v = end[1] | |
if domino[0] == end_v: | |
return True | |
domino.reverse() | |
if domino[0] == end_v: | |
return True | |
return False | |
def ai(self) -> None: | |
c = Counter(map(int, filter(str.isdigit, str(self.field + self.computer)))) | |
hand_score: List[Tuple[int, List[int]]] = list() | |
for domino in self.computer: | |
score = 0 | |
for i in domino: | |
score += c.get(i, 0) | |
hand_score.append((score, domino)) | |
hand_score.sort(reverse=True) | |
played = False | |
for _, domino in hand_score: | |
if self.is_legal(self.field[0], domino, -1): | |
self.field.insert(0, domino) | |
self.computer.remove(domino) | |
played = True | |
elif self.is_legal(self.field[-1], domino, 1): | |
self.field.append(domino) | |
self.computer.remove(domino) | |
played = True | |
if played: | |
break | |
if not played and self.stock: | |
self.computer.append(self.stock.pop()) | |
def run(self) -> None: | |
self.state = GameState.RUN | |
while self.state == GameState.RUN: | |
self.print() | |
u_input = input() | |
if next(self.order) == 'p': | |
while True: | |
try: | |
u_input = int(u_input) | |
if abs(u_input) > len(self.player): | |
raise ValueError | |
if u_input == 0: | |
if len(self.stock): | |
self.player.append(self.stock.pop()) | |
else: | |
if u_input > 0: | |
if self.is_legal(self.field[-1], self.player[u_input - 1], 1): | |
self.field.append(self.player.pop(abs(u_input) - 1)) | |
else: | |
raise Illegal | |
else: | |
if self.is_legal(self.field[0], self.player[abs(u_input) - 1], -1): | |
self.field.insert(0, self.player.pop(abs(u_input) - 1)) | |
else: | |
raise Illegal | |
break | |
except ValueError: | |
u_input = input('Invalid input. Please try again.\n') | |
except Illegal: | |
u_input = input('Illegal move. Please try again.\n') | |
self.status = 'Computer is about to make a move. Press Enter to continue...' | |
if len(self.player) == 0: | |
self.status = 'The game is over. You won!' | |
self.state = GameState.WIN | |
self.print() | |
else: | |
self.ai() | |
self.status = 'It\'s your turn to make a move. Enter your command.' | |
if len(self.computer) == 0: | |
self.status = 'The game is over. The computer won!' | |
self.state = GameState.LOSS | |
self.print() | |
if self.is_draw(): | |
self.status = 'The game is over. It\'s a draw!' | |
self.state = GameState.DRAW | |
self.print() | |
if __name__ == "__main__": | |
game = DominoGame() | |
game.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment