~piotr-machura/sweep-ai

eb7a722b975c2213db97a210ec6f453cedd193df — Piotr Machura 10 months ago 547dbb5
Update README
2 files changed, 35 insertions(+), 23 deletions(-)

M README.md
M sweep_ai/ai.py
M README.md => README.md +15 -6
@@ 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.

M sweep_ai/ai.py => sweep_ai/ai.py +20 -17
@@ 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,