This program computes the optimal strategy for an abstract version of the "Gioco del Ponte", a historical game played in Pisa.
This is a two-player, zero-sum game. Each player controls a set of
A possible initial state with
Player A control teams [1, 4, 5]
Player B control teams [2, 3, 6]
The game is composed of
Player A controls teams [1, 4, 5]
Player B controls teams [2, 3, 6]
Player A will choose first.
Round 1:
Player A chooses team 4.
Player B chooses team 3.
4 > 3, so player A wins the round.
State after round 1:
Player A controls teams [1, 5]
Player B controls teams [3, 6]
Player A has 1 point, and will choose first next round.
Round 2:
Player A chooses team 5.
Player B chooses team 6.
6 > 5, so player B wins the round.
State after round 2:
Player A controls teams [1]
Player B controls teams [3]
Player A and B are tied 1-1, and B will choose first next round.
Round 3:
Player B chooses team 3 (the only remaining one)
Player A chooses team 1 (the only remaining one)
3 > 1, so player B wins the round
State after round 3:
No more teams are left; player B wins the game 2-1.
We always describe the game state from the point of view of the player that has to make the next move. We use a structure GameState
with three members:
gs.myTeams::Vector[Int]
andgs.opponentTeams::Vector[Int]
: vectors of teams, e.g.,[1, 5]
and[3, 6]
gs.chosenTeam::Int
: this can contain the number0
, which means that the active player is the first one to choose their team this round, or an index in1, ..., length(gs.opponentTeams)
, which represents the fact that the opponent has chosen teamgs.opponentTeams[gs.chosenTeam]
and we must respond to it.
We do not keep track of the current score.
For instance, in the sample game above, at the beginning of round 2 the game state from the point of view of the active player A is
gs.myTeams: [1, 5]
gs.opponentTeams: [3, 6]
gs.chosenTeam: 0
After player A chooses team 5, the game state (from the point of view of the new active player B) becomes
gs.myTeams: [3, 6]
gs.opponentTeams: [1, 5]
gs.chosenTeam: 2
In contrast to most games, note that a player can make two consecutive moves in certain cases: for instance, in our running example, player B is the second to move in round 2, choosing team 6, and then the first to move in round 3, choosing team 3.
In this version of the game, only the ordering of team strengths matters. Hence when describing a game state we can replace gs.myTeams
and gs.opponentTeams
so that they contain consecutive numbers; in the above example we can replace the state
gs.myTeams: [3, 6]
gs.opponentTeams: [1, 5]
gs.chosenTeam: 2
with
gs.myTeams: [2, 4]
gs.opponentTeams: [1, 3]
gs.chosenTeam: 2
This reduces the number of positions to analyze.
For each game state, we can compute an optimal StrategyItem
by identifying two parameters:
-
si.bestScore
: the maximum score difference than we can achieve with optimal play. For instance, starting from an initial state with$n=3$ , if we have a strategy to win all three rounds thenbestScore
is3
, and if we can win only one round out of 3bestScore
is-1
, i.e.,1 - 2
. -
si.bestMove
: a vector containing all possible first moves that allow us to reach the maximum scoresi.bestScore
.
For instance, from the game state at the beginning of our running example
gs.myTeams: [1, 4, 5]
gs.opponentTeams: [2, 3, 6]
gs.chosenTeam: 0
one can compute that
si.bestScore: -1 # I can win only one round, with optimal play from both sides.
si.bestMove: [1, 2, 3] # It does not matter which move I play in the first round, all are equally good
Similarly, after player A has chosen team 5 in our sample playthrough, the state from the point of view of player B is
gs.myTeams: [3, 6]
gs.opponentTeams: [1, 5]
gs.chosenTeam: 2
and one can compute
gs.bestScore: 2
gs.bestMove: [1]
that is, player B can win both remaining rounds, and to do so they must answer with team 3 (the first in their list of teams). Answering with team 6 won't work.
A Strategy
is map GameState => StrategyItem
.
using PonteAI
n = 3
s = solve_game(3)
save('optimal_strategy.csv', s)
With these commands, we save in a CSV file the computed optimal strategy for each possible state of a game with n=3
, i.e.,
myTeams opponentTeams chosenTeam bestScore bestMove
[1, 2, 3] [4, 5, 6] 0 -3 [1, 2, 3]
[1, 2, 4] [3, 5, 6] 0 -3 [1, 2, 3]
[1, 2, 5] [3, 4, 6] 0 -1 [1, 2]
[1, 2, 6] [3, 4, 5] 0 -1 [1, 2, 3]
[1, 3, 4] [2, 5, 6] 0 -1 [2, 3]
[1, 3, 5] [2, 4, 6] 0 -1 [1, 2, 3]
...
...
[1] [2] 1 -1 [1]
[2] [1] 1 1 [1]
[] [] 0 0 []