190 lines
5.8 KiB
Python
Executable file
190 lines
5.8 KiB
Python
Executable file
#!/usr/bin/env python
|
|
import numpy as np
|
|
import random
|
|
|
|
BOARD_SIZE = 14
|
|
|
|
|
|
def make_board():
|
|
a = np.array([[0 for i in range(BOARD_SIZE)] for j in range(BOARD_SIZE)])
|
|
a[4, 4] = -1
|
|
a[9, 9] = -1
|
|
return a
|
|
|
|
|
|
tiles = [
|
|
np.array([[1]]),
|
|
np.array([[1], [1]]),
|
|
np.array([[1], [1], [1]]),
|
|
np.array([[1, 0], [1, 1]]),
|
|
np.array([[1], [1], [1], [1]]),
|
|
np.array([[1, 0], [1, 0], [1, 1]]),
|
|
np.array([[1, 0], [1, 1], [1, 0]]),
|
|
np.array([[1, 1], [1, 1]]),
|
|
np.array([[1, 1, 0], [0, 1, 1]]),
|
|
np.array([[1], [1], [1], [1], [1]]),
|
|
np.array([[1, 0], [1, 0], [1, 0], [1, 1]]),
|
|
np.array([[1, 0], [1, 0], [1, 1], [0, 1]]),
|
|
np.array([[1, 0], [1, 1], [1, 1]]),
|
|
np.array([[1, 1], [1, 0], [1, 1]]),
|
|
np.array([[1, 0], [1, 1], [1, 0], [1, 0]]),
|
|
np.array([[0, 1, 0], [0, 1, 0], [1, 1, 1]]),
|
|
np.array([[1, 0, 0], [1, 0, 0], [1, 1, 1]]),
|
|
np.array([[1, 1, 0], [0, 1, 1], [0, 0, 1]]),
|
|
np.array([[1, 0, 0], [1, 1, 1], [0, 0, 1]]),
|
|
np.array([[1, 0, 0], [1, 1, 1], [0, 1, 0]]),
|
|
np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]),
|
|
]
|
|
|
|
|
|
def get_permutations(which_tiles: list[int]):
|
|
permutations = []
|
|
|
|
for i, tile in enumerate(tiles):
|
|
if i not in which_tiles:
|
|
continue
|
|
|
|
rots = [np.rot90(tile, k) for k in range(4)]
|
|
flips = [np.flip(r, axis=1) for r in rots] # flip horizontally
|
|
all_orients = rots + flips # 8 orientations
|
|
|
|
seen = set()
|
|
for t in all_orients:
|
|
key = (t.shape, t.tobytes())
|
|
if key not in seen:
|
|
seen.add(key)
|
|
permutations.append((i, t))
|
|
|
|
return permutations
|
|
|
|
|
|
def can_place(board, tile, player):
|
|
placements = []
|
|
has_minus_one = False
|
|
for x in range(BOARD_SIZE):
|
|
for y in range(BOARD_SIZE):
|
|
if board[x, y] == -1:
|
|
has_minus_one = True
|
|
with np.nditer(tile, flags=["multi_index"]) as it:
|
|
for v in it:
|
|
if v == 1:
|
|
(i, j) = it.multi_index
|
|
if x + i >= BOARD_SIZE:
|
|
break
|
|
if y + j >= BOARD_SIZE:
|
|
break
|
|
if board[x + i][y + j] > 0:
|
|
break
|
|
if x + i - 1 >= 0 and board[x + i - 1][y + j] == player:
|
|
break
|
|
if y + j - 1 >= 0 and board[x + i][y + j - 1] == player:
|
|
break
|
|
if x + i + 1 < BOARD_SIZE and board[x + i + 1][y + j] == player:
|
|
break
|
|
if y + j + 1 < BOARD_SIZE and board[x + i][y + j + 1] == player:
|
|
break
|
|
else:
|
|
placements.append((x, y))
|
|
final = []
|
|
if has_minus_one:
|
|
for x, y in placements:
|
|
with np.nditer(tile, flags=["multi_index"]) as it:
|
|
for v in it:
|
|
(i, j) = it.multi_index
|
|
if v == 1 and board[x + i, y + j] == -1:
|
|
final.append((x, y))
|
|
break
|
|
else:
|
|
for x, y in placements:
|
|
with np.nditer(tile, flags=["multi_index"]) as it:
|
|
for v in it:
|
|
(i, j) = it.multi_index
|
|
if (
|
|
x + i + 1 < BOARD_SIZE
|
|
and y + j + 1 < BOARD_SIZE
|
|
and board[x + i + 1][y + j + 1] == player
|
|
):
|
|
final.append((x, y))
|
|
break
|
|
if (
|
|
x + i + 1 < BOARD_SIZE
|
|
and y + j - 1 >= 0
|
|
and board[x + i + 1][y + j - 1] == player
|
|
):
|
|
final.append((x, y))
|
|
break
|
|
if (
|
|
x + i - 1 >= 0
|
|
and y + j + 1 < BOARD_SIZE
|
|
and board[x + i - 1][y + j + 1] == player
|
|
):
|
|
final.append((x, y))
|
|
break
|
|
if (
|
|
x + i - 1 >= 0
|
|
and y + j - 1 >= 0
|
|
and board[x + i - 1][y + j - 1] == player
|
|
):
|
|
final.append((x, y))
|
|
break
|
|
return final
|
|
|
|
|
|
def do_placement(tidx, tile, placement, game_state, player):
|
|
(x, y) = placement
|
|
with np.nditer(tile, flags=["multi_index"]) as it:
|
|
for v in it:
|
|
(i, j) = it.multi_index
|
|
if v == 1:
|
|
game_state[0][x + i, y + j] = player
|
|
game_state[player].remove(tidx)
|
|
|
|
|
|
def print_game_state(game_state):
|
|
(board, p1tiles, p2tiles) = game_state
|
|
|
|
for row in board:
|
|
print(
|
|
"".join(
|
|
[
|
|
" " if x == 0 else "X" if x == 1 else "O" if x == 2 else "S"
|
|
for x in row
|
|
]
|
|
)
|
|
)
|
|
|
|
print("")
|
|
print(f"Player 1 tiles left: {p1tiles}")
|
|
print(f"Player 2 tiles left: {p2tiles}")
|
|
|
|
|
|
game_state = (
|
|
make_board(),
|
|
[i for i in range(21)],
|
|
[i for i in range(21)],
|
|
)
|
|
|
|
|
|
playing = True
|
|
player = 1
|
|
while playing:
|
|
moves = []
|
|
for tidx, tile in get_permutations(game_state[player]):
|
|
for placement in can_place(game_state[0], tile, player):
|
|
moves.append((tidx, tile, placement))
|
|
|
|
print_game_state(game_state)
|
|
print(f"player {player} has {len(moves)} options")
|
|
|
|
if len(moves) == 0:
|
|
print(f"No moves left, player {player} lost")
|
|
playing = False
|
|
continue
|
|
|
|
(tidx, tile, placement) = random.choice(moves)
|
|
do_placement(tidx, tile, placement, game_state, player)
|
|
|
|
if player == 1:
|
|
player = 2
|
|
elif player == 2:
|
|
player = 1
|