Skip to content

Commit

Permalink
Generate figure for the README
Browse files Browse the repository at this point in the history
+ Update README
  • Loading branch information
Louis Chemineau committed Jun 30, 2017
1 parent 31e1b35 commit d46828e
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 17 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ I choose tic-tac-toe because in my foolish youth I coded an "AI" full of if that
Two agents play against each other. For the first part of the program, both of them are learning to play as they play. We can see that their results are the same. But at the half, the second agent 'forget' what it has learned and also stop learning. It then become clear that one of them knows how to play and the other does not.

## Output example
| Both learning | x forget, o learns |
| :-----------: | :----------------: |
| **x** wins **52.1%** times | **x** wins **0.66%** times |
| **o** wins **47.3%** times | **o** wins **99.2%** times |
| Both learning | o forget | o forget and stop learning |
| :-----------: | :------: | :------------------------: |
| **x** wins **52.1%** times | **x** wins **78.9%** times | **x** wins **99.2%** times |
| **o** wins **47.3%** times | **x** wins **20.4%** times | **o** wins **0.66%** times |

|||
| - | - |
| ![Output 1](figures/x_o_learn--o_forget--o_stop.png) | ![Output 2](figures/x_o_learn--o_forget.png) |


## Observation
It does not take long for an agent to learn. I first, after the second agent forgot what it has learned, I was letting it learning again. The consequence was that the distinction between the two agents wins wasn't clear. That is why I also make the second agent to stop learning.

## TODO
- [ ] Make the output better. A graph showing the learning curves of the agents ?
- [x] Make the output better. A graph showing the learning curves of the agents ?
Binary file added figures/x_o_learn--o_forget--o_stop.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 figures/x_o_learn--o_forget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 84 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,60 @@ package main

import (
"fmt"
"os"
"time"

"github.com/gonum/plot"
"github.com/gonum/plot/plotter"
"github.com/gonum/plot/plotutil"
"github.com/gonum/plot/vg"
)

func nbWins(wins []entry, c boardCase) int {
count := 0
for _, e := range wins {
if e.Value == c {
count++
}
}
return count
}

type entry struct {
Timestamp int64 `json:"timestamp"`
Value boardCase `json:"value"`
}

func (e entry) String() string {
return fmt.Sprintf("%v, %v\n", e.Timestamp, e.Value)
}

func main() {
start := time.Now().UnixNano()
plot := len(os.Args) > 1 && os.Args[1] == "--plot"
// Create the board and two agent
var b board
a1 := agent{history: make(map[string]*state), gameMoves: make(map[string]*state), sign: x}
a2 := agent{history: make(map[string]*state), gameMoves: make(map[string]*state), sign: o}
a1Win := 0
a2Win := 0
// Set the number of games to play and when a1 forget and stop to learn
loopNb := 10000
loopNb := 3000
wins := make([]entry, 0, loopNb)
interWins := make([]entry, 0, loopNb/2)
for i := 0; i < loopNb; i++ {
b = newBoard()
if i >= loopNb/2 {
// After half, always make a1 forget
a1.history = make(map[string]*state)
// At half, reset the agent's win count
if i == loopNb/2 {
a2.history = make(map[string]*state)
// Display stats
fmt.Println("-------------")
fmt.Println("Both learning")
fmt.Println("-------------")
fmt.Printf("%v wins %v%% times\n", a1.sign, float64(a1Win)/float64(i)*100)
fmt.Printf("%v wins %v%% times\n", a2.sign, float64(a2Win)/float64(i)*100)
fmt.Printf("%v wins %v%% times\n", a1.sign, float64(nbWins(interWins, a1.sign))/float64(i)*100)
fmt.Printf("%v wins %v%% times\n", a2.sign, float64(nbWins(interWins, a2.sign))/float64(i)*100)
fmt.Println("====================================")
a1Win = 0
a2Win = 0
interWins = make([]entry, 0, loopNb/2)
}
}
// Play the game until the board is full or there is a winner
Expand Down Expand Up @@ -59,11 +86,13 @@ func main() {
if winnerSign == a1.sign {
a1.feed(1)
a2.feed(0)
a1Win++
wins = append(wins, entry{time.Now().UnixNano() - start, winnerSign})
interWins = append(interWins, entry{time.Now().UnixNano() - start, winnerSign})
} else if winnerSign == a2.sign {
a1.feed(0)
a2.feed(1)
a2Win++
wins = append(wins, entry{time.Now().UnixNano() - start, winnerSign})
interWins = append(interWins, entry{time.Now().UnixNano() - start, winnerSign})
} else {
a1.feed(0)
a2.feed(0)
Expand All @@ -73,6 +102,49 @@ func main() {
fmt.Println("------------------")
fmt.Println("x forget, o learns")
fmt.Println("------------------")
fmt.Printf("%v wins %v%% times\n", a1.sign, float64(a1Win)/float64(loopNb/2)*100)
fmt.Printf("%v wins %v%% times\n", a2.sign, float64(a2Win)/float64(loopNb/2)*100)
fmt.Printf("%v wins %v%% times\n", a1.sign, float64(nbWins(interWins, a1.sign))/float64(loopNb/2)*100)
fmt.Printf("%v wins %v%% times\n", a2.sign, float64(nbWins(interWins, a2.sign))/float64(loopNb/2)*100)

if plot {
generateFigure(wins, loopNb, a1, a2)
}
}

// Generate figure from the wins array
func generateFigure(wins []entry, loopNb int, a1 agent, a2 agent) {
// Create plot
p, err := plot.New()
if err != nil {
panic(err)
}
// Set plot meta data
p.Title.Text = "Both learn then O forget"
p.X.Label.Text = "Time"
p.Y.Label.Text = "Number of wins"
// Build plot data
ptsX := make(plotter.XYs, nbWins(wins, a1.sign)+1)
ptsO := make(plotter.XYs, nbWins(wins, a2.sign)+1)
countX := 0
countO := 0
for _, w := range wins[:loopNb] {
if w.Value == x {
countX++
ptsX[countX].Y = float64(countX)
ptsX[countX].X = float64(w.Timestamp)
} else if w.Value == o {
countO++
ptsO[countO].Y = float64(countO)
ptsO[countO].X = float64(w.Timestamp)
}
}
// Add data to plot
err = plotutil.AddLines(p, "X", ptsX, "O", ptsO)
if err != nil {
panic(err)
}
// Save the plot to a PNG file.
err = p.Save(4*vg.Inch, 4*vg.Inch, "points.png")
if err != nil {
panic(err)
}
}

0 comments on commit d46828e

Please sign in to comment.