Skip to content

Commit

Permalink
Add Heuristic Player and docs regarding arenas (#4)
Browse files Browse the repository at this point in the history
* Add docs regarding arenas

Signed-off-by: jparisu <[email protected]>

* Add read the docs link in readme

Signed-off-by: jparisu <[email protected]>

* Add heuristic player

Signed-off-by: jparisu <[email protected]>

---------

Signed-off-by: jparisu <[email protected]>
  • Loading branch information
jparisu authored Nov 2, 2023
1 parent 70cb7ec commit e57194a
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 31 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

**Framework** for testing **AI** system in basic or logic games.

## Documentation

The documentation is available at [Read the Docs](https://iarena.readthedocs.io/en/latest/).


## Architecture

### Interfaces
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:hidden:

/src/getting_started
/src/installation


.. toctree::
Expand Down
104 changes: 93 additions & 11 deletions docs/src/players/players.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,25 @@ The player can create its own movement or choose one from the list of possible m

.. code-block:: python
def play(
self,
position: IPosition) -> IMovement:
from IArena.interfaces.IPlayer import IPlayer
from IArena.games.Hanoi import HanoiRules, HanoiMovement, HanoiPosition
class MyPlayer(IPlayer):
def play(
self,
position: HanoiPosition) -> HanoiMovement:
# Create next movement from scratch
movement = IMovement(...)
# Create next movement from scratch
movement = IMovement(...)
# Choose one from the list of possible movements
rules = position.get_rules()
possible_movements = rules.possible_movements(position)
movement = possible_movements[...]
# Choose one from the list of possible movements
rules = position.get_rules()
possible_movements = rules.possible_movements(position)
movement = possible_movements[...]
# Return the movement
return movement
-----
Expand All @@ -73,7 +81,7 @@ If you prefer to see step by step the game playing by the player, use ``Broadcas
.. code-block:: python
from IArena.arena.GenericGame import GenericGame # or BroadcastGame
from IArena.games.Hanoi import HanoiRules, HanoiMovement, HanoiPosition
from IArena.games.Hanoi import HanoiRules
rules = HanoiRules()
my_player = MyPlayer()
Expand All @@ -85,8 +93,9 @@ If you prefer to see step by step the game playing by the player, use ``Broadcas
score = arena.play()
=================
Multiplayer games
^^^^^^^^^^^^^^^^^
=================

In games with more than 1 player, you would need another player to play against.
There are several generic players implemented, please check :ref:`random_player`.
Expand All @@ -107,3 +116,76 @@ There are several generic players implemented, please check :ref:`random_player`
players=[my_player, other_player]
)
score = arena.play()
You may want to repeat the game several times to get a better score.
For this purpose just use a loop and accumulate the score.

.. code-block:: python
from IArena.arena.GenericGame import GenericGame # or BroadcastGame
from IArena.games.Coins import CoinsRules, CoinsMovement, CoinsPosition
from IArena.players.players import RandomPlayer
rules = CoinsRules()
my_player = MyPlayer()
other_player = RandomPlayer()
score = 0
for _ in range(50):
arena = GenericGame(
rules=rules,
players=[my_player, other_player]
)
score += arena.play()
for _ in range(50):
arena = GenericGame(
rules=rules,
players=[other_player, my_player]
)
score += arena.play()
print(score)
================
Heuristic Player
================

Other way to create a player is to give a heuristic for a position, rather than a movement.
The already implement player ``HeuristicPlayer`` does check every possible future position,
by trying every possible movement in a position,
and calculates the score of the position by the method ``heuristic``.
Then, it decides the movement with the lower heuristic.

.. code-block:: python
from IArena.arena.GenericGame import GenericGame # or BroadcastGame
from IArena.games.Hanoi import HanoiRules, HanoiMovement, HanoiPosition
from IArena.players.HeuristicPlayer import HeuristicPlayer
class MyPlayer(HeuristicPlayer):
def heuristic(self, position: HanoiPosition) -> float:
# Add code to calculate the heuristic of the position
return 0.0
rules = HanoiRules()
my_player = MyPlayer()
arena = GenericGame(
rules=rules,
players=[my_player]
)
.. note::

Remember that the best movement is the one with the lowest heuristic.

Multiplayer game heuristic player
---------------------------------

One thing to take into account is that creating an heuristic for a multiplayer game,
the heuristic must be calculated as the position given is the one that the opponent will play.
Because the positions in which it applies are the next ones, and not the current ones.
33 changes: 33 additions & 0 deletions src/IArena/players/HeuristicPlayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

from IArena.interfaces.IPosition import IPosition
from IArena.interfaces.IMovement import IMovement
from IArena.interfaces.IPlayer import IPlayer
from IArena.utils.decorators import override, pure_virtual

class HeuristicPlayer(IPlayer):

@override
def play(
self,
position: IPosition) -> IMovement:
# Get rules of the game
rules = position.get_rules()

# Get all movements
movements = rules.possible_movements(position)

# Calculate heuristic for all possible positions from every possible movement
values = [
self.heuristic(
rules.next_position(movement, position))
for movement
in movements]

# Return the best movement
return movements[values.index(min(values))]

@pure_virtual
def heuristic(
self,
position: IPosition) -> float:
pass
28 changes: 8 additions & 20 deletions src/IArena/players/players.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,40 @@

import random
# from typing import override

from IArena.interfaces.IPosition import IPosition
from IArena.interfaces.IGameRules import IGameRules
from IArena.interfaces.IMovement import IMovement
from IArena.interfaces.PlayerIndex import PlayerIndex
from IArena.interfaces.IPlayer import IPlayer
from IArena.utils.decorators import override

class DummyPlayer(IPlayer):

def __init__(
self,
rules: IGameRules,
player_index: PlayerIndex):
self.rules_ = rules
self.player_index_ = player_index


class FirstPlayer(DummyPlayer):
class FirstPlayer(IPlayer):

@override
def play(
self,
position: IPosition) -> IMovement:
return self.rules_.possible_movements(position)[0]
return position.get_rules().possible_movements(position)[0]


class LastPlayer(DummyPlayer):
class LastPlayer(IPlayer):

@override
def play(
self,
position: IPosition) -> IMovement:
return self.rules_.possible_movements(position)[-1]
return position.get_rules().possible_movements(position)[-1]


class RandomPlayer(DummyPlayer):
class RandomPlayer(IPlayer):

@override
def play(
self,
position: IPosition) -> IMovement:
return random.choice(self.rules_.possible_movements(position))
return random.choice(position.get_rules().possible_movements(position))


class PlayablePlayer(DummyPlayer):
class PlayablePlayer(IPlayer):

SeparatorN = 40

Expand All @@ -55,7 +43,7 @@ def play(
self,
position: IPosition) -> IMovement:

possibilities = list(self.rules_.possible_movements(position))
possibilities = list(position.get_rules().possible_movements(position))

print ("=" * PlayablePlayer.SeparatorN)
print (f"Next player: {position.next_player()}")
Expand Down

0 comments on commit e57194a

Please sign in to comment.