-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.go
157 lines (137 loc) · 3.42 KB
/
game.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package main
import (
"fmt"
"math"
"math/rand"
)
func createSessions(players []player) {
for {
// user input
var newSess bool
for {
fmt.Printf("Create a session? (t/f): ")
_, err := fmt.Scanf("%t", &newSess)
if err == nil {
break
}
}
if !newSess {
break
}
// start a new session
fmt.Print("available players are: \n")
for i, p := range players {
fmt.Printf("#%v %v \n", i, p.name)
}
var i1, i2, n int
if len(players) > 2 { // more than 2 available players, choose 2
for {
fmt.Printf("pick two players (# #): ")
_, err := fmt.Scanf("%d%d", &i1, &i2)
if err == nil {
break
}
}
} else {
i1, i2 = 0, 1
}
for {
fmt.Printf("how many episodes: ")
_, err := fmt.Scanf("%d", &n)
if err == nil {
break
}
}
// run session
fmt.Printf("*** Session starts: %v and %v play %v episodes *** \n", players[i1].name, players[i2].name, n)
runSession(&playerPair{players[i1], players[i2]}, n)
}
return
}
func runSession(ps *playerPair, nEpisodes int) {
// set up reporting parameters
r := false // report more frequently
v := false // robot is verbose
if ps[0].being != ps[1].being { // human vs robot
r = true // report more frequently
for {
fmt.Printf("set robot to verbose? (t/f): ")
_, err := fmt.Scanf("%t", &v)
if err == nil {
break
}
}
}
for i := range ps {
if ps[i].being == "robot" {
ps[i].mind.verb = v
}
}
// run episodes
for episode := 0; episode < nEpisodes; episode++ {
epiNum := episode + 1 // epiNum starts from 1 which is more human readable
if math.Mod(float64(epiNum), nPrintEpisode) == 0 && ps[0].being == ps[1].being {
fmt.Printf("episode #%v \n", epiNum)
}
runEpisode(ps, r, episode == 0)
}
// robot export values
for i := range ps {
if ps[i].being == "robot" {
exportValues(ps[i].name, ps[i].mind.values)
exportValueHistory(ps[i].name, ps[i].mind.demohist)
}
}
fmt.Printf("*** Session ends - %v won %v times / %v won %v times *** \n\n", ps[0].name, ps[0].wins, ps[1].name, ps[1].wins)
return
}
// run an episode and let players (if robot) remember what they've learnt
func runEpisode(ps *playerPair, report, firstEpisode bool) {
var loc location
var env environment
if printSteps { // global const to force reporting
report = true
}
env.initializeEnvironment()
// randomly assign 0 or 1 as the first player ("x")
first := rand.Perm(2)[0]
second := 1 - first
// first player uses "x"
ps[first].symbol = "x"
ps[second].symbol = "o"
if report {
fmt.Printf("\n %v(%v) starts first \n", ps[first].name, ps[first].symbol)
}
s := "o" // current player
for !env.gameOver {
// switch player and take action
if s == "o" {
s = "x"
loc = ps[first].playerActs(env)
} else {
s = "o"
loc = ps[second].playerActs(env)
}
// update environment by the action
env.updateGameStatus(loc, s)
// update state history and remember the demo states
for i := range ps {
// The same board is encoded differently by the two players;
// each location is viewed not as "x" or "o", but instead as Me or You.
state := boardToState(&env.board, ps[i].symbol)
ps[i].updateStateSequence(state)
}
}
if firstEpisode {
for i := range ps {
ps[i].getDemoStates()
}
}
if report {
env.summarizeEpisode(&ps[first], &ps[second])
}
// grow some brain
ps[first].updatePlayerRecord(env)
ps[second].updatePlayerRecord(env)
return
}