initial commit
This commit is contained in:
commit
bc71e758cf
7 changed files with 247 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
use flake
|
||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
.direnv
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.13
|
||||
0
README.md
Normal file
0
README.md
Normal file
165
blokus.py
Executable file
165
blokus.py
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
#!/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[tuple[int,int]]):
|
||||
permutations = []
|
||||
for i,tile in enumerate(tiles):
|
||||
if i not in which_tiles:
|
||||
continue
|
||||
permutations.append((i,tile))
|
||||
permutations.append((i,np.rot90(tile)))
|
||||
permutations.append((i,np.rot90(np.rot90(tile))))
|
||||
permutations.append((i,np.rot90(np.rot90(np.rot90(tile)))))
|
||||
permutations.append((i,np.flip(tile)))
|
||||
permutations.append((i,np.flip(np.rot90(tile))))
|
||||
permutations.append((i,np.flip(np.rot90(np.rot90(tile)))))
|
||||
permutations.append((i,np.flip(np.rot90(np.rot90(np.rot90(tile))))))
|
||||
|
||||
unique_arrays = []
|
||||
for arr in permutations:
|
||||
if not any(np.array_equal(arr, u) for u in unique_arrays):
|
||||
unique_arrays.append(arr)
|
||||
|
||||
return unique_arrays
|
||||
|
||||
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
|
||||
|
||||
|
||||
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764167966,
|
||||
"narHash": "sha256-nXv6xb7cq+XpjBYIjWEGTLCqQetxJu6zvVlrqHMsCOA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5c46f3bd98147c8d82366df95bbef2cab3a967ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
26
flake.nix
Normal file
26
flake.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{ nixpkgs, ... }:
|
||||
let
|
||||
eachSystem =
|
||||
f:
|
||||
nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (system: f nixpkgs.legacyPackages.${system});
|
||||
in
|
||||
{
|
||||
devShells = eachSystem (pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
(pkgs.python3.withPackages (ppkgs: [
|
||||
ppkgs.numpy
|
||||
ppkgs.torch
|
||||
]))
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue