Skip to content

Commit

Permalink
Merge pull request #3 from Tomansion/player_choice_pull_request
Browse files Browse the repository at this point in the history
Player choice pull request
  • Loading branch information
Tomansion authored Apr 22, 2024
2 parents ba20832 + c415f4d commit 6759395
Show file tree
Hide file tree
Showing 16 changed files with 638 additions and 352 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ __pycache__/
htmlcov/
build_package/
dist/
SantorinAI.egg-info/
SantorinAI.egg-info/
build/
142 changes: 108 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,119 @@
![Graphical output example](./images/headban.png)

# SantorinAI

AI Player tester for the Santorini game

## How to use

### 1. Install

With pip:

```bash
pip install --upgrade santorinai
```

You can also clone the repository and install it manually:

```bash
git clone https://github.com/Tomansion/SantorinAI.git
cd SantorinAI
pip install -r requirements.txt
pip install .
```

### 2. Create a player

Create an override of the `Player` class in the `santorinai.player.py` file.

```python
from santorinai.player import Player
from santorinai import Board, Pawn, Player
from random import choice


class MyPlayer(Player):
"""
My player description
"""

def __init__(self, player_number):
super().__init__(player_number)
# Do some initialization here

def name(self):
"""
Provide a name to your player
"""
return "My player name"

# Placement of the pawns
def place_pawn(self, board, pawn):
my_pawn_number = pawn.number # Between 1 and 6 depending on the game mode
my_player_number = pawn.player_number # Between 1 and 3 depending on the game mode
def place_pawn(self, board: Board, pawn: Pawn):
"""
Place a pawn
Args:
board (Board): A copy of the current board state
pawn (Pawn): The pawn to place
Returns:
tuple: A position on the 5x5 board
"""
# Do some magic here to choose a position
my_choice = (2, 3) # A position on the 5x5 board
my_choice = choice(board.get_possible_movement_positions(pawn))

return my_choice
return my_choice # A position on the 5x5 board

# Movement and building
def play_move(self, board, pawn):
my_pawn_pos = pawn.pos
def play_move(self, board: Board):
"""
Play a move
Args:
board (Board): A copy of the current board state
Returns:
tuple: (pawn_number, move_position, build_position)
"""

board_array = board.board # A 5x5 array of integers representing the board
# This function is called when the player needs to play

my_pawn_1 = board.get_playing_pawn(1)
my_pawn_2 = board.get_playing_pawn(2)

board.board # A 5x5 array of integers representing the board
# 0: empty
# 1: tower level 1
# 2: tower level 2
# 3: tower level 3
# 4: terminated tower

pawns = board.pawns # The other pawns on the board

# Do some magic here to choose a position
my_move_position = ( # Moving top right
my_pawn_pos[0] + 1,
my_pawn_pos[1] + 1
)
my_pawn_to_move_choice = choice([my_pawn_1, my_pawn_2])
my_pawn_possible_moves = board.get_possible_movement_and_building_positions(
my_pawn_to_move_choice
)

my_build_position = ( # Building right (relative to the new position)
my_move_position[0] + 1,
my_move_position[1] + 0
)
if len(my_pawn_possible_moves) == 0:
return None, None, None

return my_move_position, my_build_position
my_move_and_build_choice = choice(my_pawn_possible_moves)

my_move_position = my_move_and_build_choice[0]
my_build_position = my_move_and_build_choice[1]

# Return the selected pawn to move (1 or 2), the move position
# and the build position
# If my move is not valid, the tester will consider it as a forfeit
return my_pawn_to_move_choice.order, my_move_position, my_build_position
```

Check our random players example in [our player examples folder](./santorinai/player_examples/) to help you create your own.
Check our random players example in [our player examples folder](./santorinai/player_examples/) to help you create your own.

### 3. Test your player

```python
from santorinai.tester import Tester
from santorinai.player_examples.random_player import RandomPlayer
from santorinai import Tester, RandomPlayer
from my_player import MyPlayer

# Init the tester
Expand All @@ -86,29 +123,56 @@ tester.delay_between_moves = 0.1 # Delay between each move in seconds
tester.display_board = True # Display a graphical view of the board in a window

# Init the players
my_player = MyPlayer()
random_payer = RandomPlayer()

# Play 100 games
tester.play_1v1(my_player, random_payer, nb_games=100)
my_player = MyPlayer(1)
random_payer = RandomPlayer(2)

# Play some games
wins, details = tester.play_1v1(
player1=my_player,
player2=random_payer,
nb_games=5,
)
print(wins)
print(details)
```

Output example:

```
Results:
Player Randy Random won 10 times
Player My player won 70 times
Player My player won 4 times (80%)
Player Randy Random won 1 time (20%)
# Wins
{
"My player name": 4,
"Randy Random": 1,
}
# Details
{
"My player name": {
"The player pawn reached the top of a tower.": 3,
"The next player is stuck, the game is over.": 1,
},
"Randy Random": {
"The player pawn reached the top of a tower.": 1
},
}
```

Graphical output example:
![Graphical output example](./images/board_image.png)

## Board utilities

We provide some utilities to help you manipulate the board.

```python
# Game informations
# Game information
board.nb_players # Number of players in the game (2 or 3)
board.nb_pawns # Number of pawns (4 or 6) depending on the game mode
board.pawn_turn # Pawn that is currently playing
board.player_turn # The number of the player currently playing (between 1 and 3)
board.turn_number # Number of turn played since the beginning of the game

# Pawns
Expand All @@ -117,7 +181,6 @@ pawn = board_pawns[0] # The first pawn on the board
pawn.pos # The position a pawn on the board (x, y), or (None, None) if it is not placed yet
pawn.number # The number of the pawn on the board (between 1 and 6) depending on the game mode
pawn.player_number # The number of the player owning the pawn (between 1 and 3) depending on the game mode
playing_pawn = board.get_playing_pawn() # The pawn that is currently playing


# Board
Expand All @@ -134,7 +197,9 @@ available_build_positions = board.get_possible_building_positions(pawn)

# Board control
board.place_pawn(pos) # Place the current playing pawn on the board
board.play_move(move_position, build_position) # Play a move (move and build) with the current playing pawn
board.play_move(pawn.order, move_position, build_position) # Play a move (move and build) with the current playing pawn, and go to the next turn
board.is_game_over() # True if the game is over
board.winner_player_number # The number of the player who won the game

# Other
board.is_position_valid(position)
Expand All @@ -157,3 +222,12 @@ update_board(window, board)
Creator of Santorini: [Roxley Games](https://roxley.com/)

Board 2D Gui library: [PySimpleGUI](https://www.pysimplegui.org/en/latest/)

## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details

## Contributors

- [Tomansion](http://github.com/tomansion)
- [daniPclos](https://github.com/daniPclos) (Implementation of the pawn selection on move feature)
Empty file added examples/__init__.py
Empty file.
34 changes: 24 additions & 10 deletions examples/player_list_evaluator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import sys

sys.path.append("..")

from santorinai.tester import Tester
from santorinai.player_examples.random_player import RandomPlayer
from santorinai.player_examples.first_choice_player import FirstChoicePlayer
Expand All @@ -11,32 +7,44 @@
# It will display a table with the results of each player against each other player.

players_classes = [
BasicPlayer,
RandomPlayer,
FirstChoicePlayer,
BasicPlayer,
]

# Init the tester
tester = Tester()
tester.verbose_level = 0 # 0: no output, 1: Each game results, 2: Each move results# tester.delay_between_moves = 0.5 # Delay between each move in seconds
tester.verbose_level = 0
# Verbose level:
# 0: no output,
# 1: Each game results
# 2: Each move results

# tester.delay_between_moves = 0.5 # Delay between each move in seconds
# tester.display_board = True # Display a graphical view of the board in a window

nb_games = 1000
results = {} # We will count the number of victories for each player

# Initialize global victory type evaluator
dic_global_win_lose_type = {}

# Match all combinations of players
for i, player1_class in enumerate(players_classes):
# Get the name of the player
player1_name = player1_class().name()
player1_name = player1_class(i).name()
results[player1_name] = {}

for j, player2_class in enumerate(players_classes):
if i == j:
continue

# Init the players
p1 = player1_class()
p2 = player2_class()
p1 = player1_class(1)
p2 = player2_class(2)

# Initialize victory type evaluator dic
dic_win_lose_type = {p1.name(): {}, p2.name(): {}}

# Get the name of the player 2
player2_name = p2.name()
Expand All @@ -45,9 +53,15 @@
print(f"\n\nPlaying {player1_name} vs {player2_name}:")

# Play 100 games
victories_number = tester.play_1v1(p1, p2, nb_games=nb_games)
victories_number, dic_global_win_lose_type[f"{p1.name()}vs{p2.name()}"] = (
tester.play_1v1(
p1, p2, nb_games=nb_games, dic_win_lose_type=dic_win_lose_type
)
)

results[player1_name][player2_name] = victories_number[player1_name]

print(f"dic_global_win_lose_type = \n{dic_global_win_lose_type}")

print()
print("Results:")
Expand Down
11 changes: 5 additions & 6 deletions examples/random_players_match.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import sys
sys.path.append('..')

from santorinai.tester import Tester
from santorinai.player_examples.random_player import RandomPlayer
from santorinai.player_examples.first_choice_player import FirstChoicePlayer
from santorinai.player_examples.basic_player import BasicPlayer

# Init the tester
tester = Tester()
Expand All @@ -12,8 +10,9 @@
tester.display_board = True # Display a graphical view of the board in a window

# Init the players
my_player = FirstChoicePlayer()
random_payer = RandomPlayer()
my_player = FirstChoicePlayer(1)
basic_player = BasicPlayer(1)
random_payer = RandomPlayer(2)

# Play 100 games
tester.play_1v1(my_player, random_payer, nb_games=100)
tester.play_1v1(basic_player, random_payer, nb_games=100)
7 changes: 7 additions & 0 deletions santorinai/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .board import Board
from .player import Player
from .tester import Tester
from .pawn import Pawn
from .player_examples.random_player import RandomPlayer
from .player_examples.first_choice_player import FirstChoicePlayer
from .player_examples.basic_player import BasicPlayer
Loading

0 comments on commit 6759395

Please sign in to comment.