@@ 5,8 5,7 @@ Sweep AI is a minesweeper clone enhanced with deep learning capabilities. While
for us?
This variant enhances the classic minesweeper gameplay with AI-powered hints, compiling the AI player with
-[neat-python](https://github.com/CodeReclaimers/neat-python). The interface is built using
-[pygame](https://www.pygame.org/) and
+[keras](https://keras.io/). The interface is built using [pygame](https://www.pygame.org/) and
[pygame-menu](https://github.com/ppizarror/pygame-menu).
# Installation
@@ 16,6 15,7 @@ The safest way to clone the repo and install the game within a virtual environme
python3 -m venv .venv
source .venv/bin/activate
pip install .
+# Run the game
python3 -m sweep-ai
```
@@ 23,8 23,17 @@ For development, use [poetry](https://python-poetry.org/):
```bash
# In the project's directory
poetry install
-# Run unit tests
-poetry run pytest
-# Play the game
-poetry run sweep-ai
+poetry run pytest # Run unit tests
+poetry run sweep-ai # Play the game
```
+
+# Cache
+The players' brains and training data are cached and looked for in the following order:
+
+1. `$SWEEP_CACHE/sweep`
+2. `$XDG_CACHE_HOME/sweep`
+3. `$CWD/sweep`
+
+Run the game with `--clean-cache` flag to remove the cache directory. Use `--no-cache` if you don't want to use the
+cached models (cached training data is still used). To generate a plot of player's loss after training add the `--plot`
+flag.
@@ 20,15 20,12 @@ class Player:
size: the size of the board on which the player has been trained.
"""
CACHEDIR = Path(
- os.environ.get('SWEEP_BRAIN_CACHE')
+ os.environ.get('SWEEP_CACHE')
or os.environ.get('XDG_CACHE_HOME') or '.',
- 'sweep_brains',
- 'conv',
+ 'sweep',
)
CACHE = True
PLOT = False
- X_TRAIN = Path(__file__).parent.joinpath('x.npy')
- Y_TRAIN = Path(__file__).parent.joinpath('y.npy')
def __init__(self, size: int, difficulty: float):
"""Constructs a new `Player`."""
@@ 37,6 34,7 @@ class Player:
self.trained = False
self.brain_location = self.CACHEDIR.joinpath(
+ 'conv',
f'S{self.size}_D{self.difficulty}',
)
if self.CACHE and self.brain_location.exists():
@@ 71,6 69,7 @@ class Player:
strides=1,
activation=None,
), # -> size x size
+ # u-net
])
self.brain.compile(
keras.optimizers.Adam(learning_rate=0.001),
@@ 113,28 112,33 @@ class Player:
output = self.brain.predict(
np.stack([self.training_data(state)[0]]),
)[0]
- # print(output)
output = np.where(
(state.revealed == 1) | (state.flagged == 1),
0,
output,
)
- # print(output)
pos = np.unravel_index(np.argmax(output, axis=None), output.shape)
- # print(pos)
return pos
def train(self):
"""Recrete and train the AI brain of this player."""
self.brain.summary()
- if self.X_TRAIN.exists() and self.Y_TRAIN.exists():
- x_train = np.load(self.X_TRAIN)
- y_train = np.load(self.Y_TRAIN)
+ x_loc = self.CACHEDIR.joinpath(
+ 'train',
+ f'x_S{self.size}_D{self.difficulty}.npy',
+ )
+ y_loc = self.CACHEDIR.joinpath(
+ 'train',
+ f'y_S{self.size}_D{self.difficulty}.npy',
+ )
+ if x_loc.exists() and y_loc.exists():
+ x_train = np.load(x_loc)
+ y_train = np.load(y_loc)
else:
- x_train = np.ndarray((0, 10, 10))
- y_train = np.ndarray((0, 10, 10))
- while len(x_train) < 3e4:
+ x_train = np.ndarray((0, self.size, self.size))
+ y_train = np.ndarray((0, self.size, self.size))
+ while len(x_train) < 1e4:
state = State(self.size, self.difficulty)
# CLick on a guaranteed empty space
x_click, y_click = np.transpose(np.nonzero(state.near == 0))[0]
@@ 153,9 157,8 @@ class Player:
axis=0,
)
state.click(*state.cheat())
- print(len(x_train))
- np.save('x', x_train)
- np.save('y', y_train)
+ np.save(x_loc, x_train)
+ np.save(y_loc, y_train)
history = self.brain.fit(
x_train,