Skip to content

Commit

Permalink
cleanup and results
Browse files Browse the repository at this point in the history
  • Loading branch information
Krasto committed Jan 22, 2024
1 parent f9487ac commit c510cc9
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 51 deletions.
62 changes: 31 additions & 31 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python application
# name: Python application

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# on:
# push:
# branches: [ "main" ]
# pull_request:
# branches: [ "main" ]

permissions:
contents: read
# permissions:
# contents: read

jobs:
build:
# jobs:
# build:

runs-on: ubuntu-latest
# runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Evaluate
if: contains(github.event.head_commit.message, 'EVAL')
run: |
python -m ci_quixo
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
title: Generate nuove foto di eval
commit-message: updated results picture
# steps:
# - uses: actions/checkout@v3
# - name: Set up Python 3.10
# uses: actions/setup-python@v3
# with:
# python-version: "3.10"
# - name: Install dependencies
# run: |
# python -m pip install --upgrade pip
# pip install pytest
# if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
# - name: Evaluate
# if: contains(github.event.head_commit.message, 'EVAL')
# run: |
# python -m ci_quixo
# - name: Create Pull Request
# uses: peter-evans/create-pull-request@v5
# with:
# title: Generate nuove foto di eval
# commit-message: updated results picture
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,40 @@ The game Quixo is a Tic-Tac-Toe variant, played on a five-by-five board of cubes
## Navigate through the code

- `custom_game.py`: Wrapper around Game class, with some utility methods and symmetry (canonical representation) handling
- `minmax.py` and `mcts.py`: Player's files
- `minmax.py`: Minmax implementation
- Features:
- Depth-Limited
- Alpha-Beta Pruning
- Hash-Tables
- Different Pruning Levels:
- Pruning lvl 0 - No Pruning, at each node consider all possible moves
- Pruning lvl 1 - At each node consider only the moves that land on different boards
- Pruning lvl 2 - At each node consider only the moves that land on different boards and that are not symmetric to each other
- Pruning lvl 3 - At each node consider only the moves that land on different boards and that are not symmetric to each other and that are not have been already visited on a higher level (not really that useful since we limit to a depth of 3, so there really is not a chance for a cycle to happen)
- `mcts.py`: MonteCarlo Tree Search implementation
- Features:
- Random
- w/ Heuristic
- `__main__.py`: containis the code to perform the evaluation

## Results obtained

### Vs Random

| Agent | Games | Win Rate (%) | Average Time per Game (s) | Average Time per Move (s) | Total Number of Moves |
|:-------------------------------------------------:|:-----:|:------------:|:-------------------------:|:-------------------------:|:---------------------:|
| MinMax(AB,D2,P1) - αβ + Depth 2 + Pruning lvl 1 | 100 | 100 | 0.61 | 0.07 | 831 |
| MinMax(AB,D3,P2) - αβ + Depth 3 + Pruning lvl 2 | 100 | 100 | 11.97 | 1.47 | 816 |
| MCTS(R500) - 500 Games with Random Moves | 100 | 84 | 8.91 | 0.90 | 985 |
| MCTS(H500) - 500 Games with Heuristic | 100 | 79 | 52.45 | 4.42 | 1186 |

![Win Rates](./ci_quixo/results/players_wr.png)

![Time Comparison](./ci_quixo/results/time_comparison.png)

## Possible Improvements

- [ ] Minmax w/ RankCut
- [ ] Minmax w/ Singular Moves (should be easy and fast-enough using Hash Tables)
- [ ] Minmax w/ Parallelization
- [ ] Parallelization both on Minmax and MCTS
- [ ] Lookup Tables for starting positions
29 changes: 16 additions & 13 deletions ci_quixo/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
from ._players import *
from .main import Player
from .helper import evaluate, Result, gen_plots
from pprint import pprint
import dill, multiprocessing
dill.Pickler.dumps, dill.Pickler.loads = dill.dumps, dill.loads
multiprocessing.reduction.ForkingPickler = dill.Pickler
multiprocessing.reduction.dump = dill.dump

GAMES = 10
GAMES = 100
HIDE_PBAR = True
PARALLELIZE = True
DISPLAY_RES = True

minmax2 = MinMaxPlayer()
minmax3 = MinMaxPlayer(3, pruning=2)
Expand All @@ -22,32 +23,34 @@


def test_minmax_vs_random() -> Result:
return evaluate(minmax2, RandomPlayer(), games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(minmax2, RandomPlayer(), games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)

def test_minmax3_vs_random() -> Result:
return evaluate(minmax3, RandomPlayer(), games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(minmax3, RandomPlayer(), games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)

def test_mcts_r_vs_random() -> Result:
return evaluate(mcts_r, RandomPlayer(), games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(mcts_r, RandomPlayer(), games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)


def test_mcts_h_vs_random() -> Result:
return evaluate(mcts_h, RandomPlayer(), games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(mcts_h, RandomPlayer(), games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)


def test_minmax_vs_mcts_random() -> Result:
return evaluate(minmax2, mcts_r, games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(minmax2, mcts_r, games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)

def test_minmax_vs_mcts_heuristic() -> Result:
return evaluate(minmax2, mcts_h, games=GAMES, display=True, hide_pbar=HIDE_PBAR)
return evaluate(minmax2, mcts_h, games=GAMES, display=DISPLAY_RES, hide_pbar=HIDE_PBAR)

def call(it: callable) -> Result:
return it()

if __name__ == "__main__":
evals = [test_minmax_vs_random, test_minmax3_vs_random, test_mcts_r_vs_random, test_mcts_h_vs_random, test_minmax_vs_mcts_random, test_minmax_vs_mcts_heuristic]

with multiprocessing.Pool() as p:
RESULTS = p.map(call, evals[:-2])

from tqdm import tqdm
evals = [test_minmax_vs_random, test_minmax3_vs_random, test_mcts_r_vs_random, test_mcts_h_vs_random, test_minmax_vs_mcts_random, test_minmax_vs_mcts_heuristic][:]
if PARALLELIZE:
with multiprocessing.Pool() as p:
RESULTS = list(tqdm(p.imap(call, evals), total=len(evals), unit="test", desc=f"Testing with {GAMES} games each"))
else:
RES = [it() for it in tqdm(evals, desc="Evaluating", unit="eval")]
gen_plots(RESULTS)
12 changes: 7 additions & 5 deletions ci_quixo/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
if TYPE_CHECKING:
from .custom_game import CompleteMove

import time
import time, dill
import numpy as np
from tqdm.auto import trange
from os import path
Expand Down Expand Up @@ -88,6 +88,8 @@ def evaluate(p1: "Player", p2: "Player" = None, games: int = 10, display: bool =


def gen_plots(results: list[Result]) -> None:
with open(path.join(PLOT_FOLDER, "results.bkp.pkl"), "wb+") as f:
dill.dump(results, f)
plot_time_comparison(results)
plot_wr_vs_random(results)

Expand All @@ -107,8 +109,8 @@ def plot_wr_vs_random(results: list[Result]) -> None:
"Overall": wr
}

x = np.arange(len(player_names)) * 1.6
width = 0.30 # the width of the bars
x = np.arange(len(player_names)*2, step=2)
width = 0.50 # the width of the bars


fig, ax = plt.subplots(layout='constrained')
Expand All @@ -122,7 +124,7 @@ def plot_wr_vs_random(results: list[Result]) -> None:
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Win Rate (%)')
ax.set_title('Agents Win Rate vs Random')
ax.set_xticks(x + width, player_names)
ax.set_xticks(x + width, player_names, rotation=-20)
ax.legend(loc='upper left', ncols=3)
ax.set_ylim(0, 120)

Expand All @@ -135,7 +137,7 @@ def plot_time_comparison(results: list[Result]) -> None:
ys = [r.avg_time for r in results]
xs = [r.p1.short_name for r in results]
plt_title = "Average Seconds per Game (vs Random)"
plt.plot(xs, ys, 'o')
plt.plot(xs, ys, marker='o', linestyle='--')
plt.title(plt_title)
plt.ylabel("seconds")
plt.yticks(ys)
Expand Down
Binary file modified ci_quixo/results/players_wr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ci_quixo/results/results.bkp.pkl
Binary file not shown.
Binary file modified ci_quixo/results/time_comparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c510cc9

Please sign in to comment.