/
ponder.cpp
executable file
·256 lines (210 loc) · 8.12 KB
/
ponder.cpp
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//
// Ponder This May 2015 Solution by Richard George (rg.george1@gmail.com)
//
#include <iostream>
#include <fstream>
#include <set>
#include <string>
#include <sstream>
#include "GameState.h"
#include "ponder.h"
// Solver driver
int explore_game(const GameState &starting)
{
// If the starting condition is not in the game, abort
if (starting.isValid()==false) return 0;
std::set<GameState> known; // states already visited
std::set<GameState> to_explore; // states to play
std::set<GameState> leafs; // novel states reached from states in to_explore
to_explore.insert(starting); // add the starting state to the states to explore
bool useful = true;
int rounds = 0;
while (true)
{
useful = explore_game2(known,to_explore,leafs,rounds); // make one iteration
rounds++;
if (useful==false) break;
known.insert(to_explore.begin(),to_explore.end()); // add explored states to known states
std::swap(to_explore,leafs); // swap leafs into to_explore for next round
std::set<GameState> empty;
std::swap(leafs,empty); // empty the list of leaf states
}
// the number of iterations before we reached an end-game state
return rounds;
}
bool explore_game2(std::set<GameState> &known,std::set<GameState> &to_explore,std::set<GameState> &leafs,int depth)
{
// Performs one round of iteration towards the solution
//
// known - set of visited game states
// to_explore - set of input states to play
// leafs - set of new game states generated by possible moves on 'to_explore'
// iterator over states to_explore
std::set<GameState>::iterator it;
// Flag to monitor if a move took place
bool moved=false;
// Iterate over each input state that has not yet been played
for (it=to_explore.begin();it!=to_explore.end();it++)
{
// If the input state has already been played, skip it
std::set<GameState>::iterator i0 = known.find(*it);
if (i0!=known.end()) continue;
// Generate three new game states m1,m2,m3
// which result from playing move 0,1,2 on the input state
// If any of the states will end the game, stop iterating and return false
GameState m1(*it,0);
if (m1.isValid()==false) return false;
GameState m2(*it,1);
if (m2.isValid()==false) return false;
GameState m3(*it,2);
if (m3.isValid()==false) return false;
// If the resulting state is novel, add it to the output set of leaf states
// which will be explored in the next iteration, and flag that a move
// has taken place
std::set<GameState>::iterator i1=known.find(m1);
// if m1 is not in 'known' set
if (i1==known.end())
{
leafs.insert(m1);
moved=true;
}
std::set<GameState>::iterator i2=known.find(m2);
// if m2 is not in 'known' set
if (i2==known.end())
{
leafs.insert(m2);
moved=true;
}
std::set<GameState>::iterator i3=known.find(m3);
// if m3 is not in 'known' set
if (i3==known.end())
{
leafs.insert(m3);
moved=true;
}
}
// returns 'true' if another iteration is justified and 'false' otherwise
return moved;
}
// Main program
int main(int argc,char **argv,char **envp)
{
std::cout << std::endl;
std::cout << "IBM Ponder This May 2015 Challenge" << std::endl;
std::cout << "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-" << std::endl << std::endl;
std::cout << "by Richard George (rg.george1@gmail.com)" << std::endl << std::endl;
if (sanity()==false)
{
std::cout << "Bug in program" << std::endl;
return 1;
}
switch (argc)
{
case 1:
solve_problem("solution.txt");
break;
case 2:
solve_problem(argv[1]);
break;
default:
std::cout << argv[0] << " solution.txt" << std::endl;
std::cout << std::endl;
std::cout << "Solve betting problem and write result to <solution.txt>" << std::endl;
break;
}
return 0;
}
bool sanity()
{
GameState g0(5, 7, 7); // Not a valid game state
GameState g1(1, 2, 254); // Finish in 1 round
GameState g2(2, 3, 254); // ... in 2 rounds
GameState g3(3, 4, 254); // ... in 3 rounds
GameState g4(4, 5, 254); //
GameState g5(4, 9, 254); // These come from a (slower) python implementation
GameState g6(8, 11, 254);
GameState g7(12, 19, 254);
GameState g8(8, 35, 254);
GameState g9(1, 4, 6); // From webpage, known to finish in 15 minutes = at round 2
if (explore_game(g0)!=0) return false; // Verify that we get the expected number
if (explore_game(g1)!=1) return false; // of rounds for given input states
if (explore_game(g2)!=2) return false; //
if (explore_game(g3)!=3) return false; // report an error if not
if (explore_game(g4)!=4) return false;
if (explore_game(g5)!=5) return false;
if (explore_game(g6)!=6) return false;
if (explore_game(g7)!=7) return false;
if (explore_game(g8)!=8) return false;
if (explore_game(g9)!=2) return false;
return true;
}
void solve_problem(const char *fname)
{
int i,j,k; // monies of players 1,2,3
int a=0,s=0; // s counts number of solutions found, a counts number of trials
int n=0; // n manages display of progress
int l; // l is the minimum length, in rounds, of the game starting at (i,j,k)
bool found=false; // A flag to note when a solution is found for display purposes
std::set<std::pair<GameState,int> > results;
// Outer loop over the money of player one
for (i=3;i<256;i++)
{
// Print a dot whilst searching, but print a star if the last search was successful
std::cout << (found ? '*' : '.') << std::flush;
found = false;
// Scroll the screen nicely to demonstrate activity
n++;
if (((n % 60)==0) && (n!=0))
{
double progress = double(a)/double(255.0*254.0*253.0/6.0);
std::cout << " " << i << "/255 (" << float(int(progress*1000.0))/10.0;
std::cout << "%)" << std::endl;
}
// Inner loops over the money of players two and three
for (j=2;j<i;j++)
{
for (k=1;k<j;k++)
{
// Create a gamestate for the current value of (i,j,k)
GameState g(i,j,k);
// Measure the number of moves required to complete game (i,j,k)
l = explore_game(g);
// If the game ends on turn 11 or later (counted from zero)
// then the game will take at least 60 minutes
if (l>=11)
{
// Store the result for printing later
results.insert(std::pair<GameState,int>(g,l));
// s counts the total number of solutions found
s++;
// flag that we found a solution
found = true;
}
// 'a' counts the total number of iterations
a++;
}
}
// Signal that we've searched all combinations
if (i==255)
{
std::cout << " 255/255 (100%)" << std::endl;
}
}
// Report the results
i = 1; // Recycle variable 'i' now to count solutions
std::cout << std::endl << "Found " << s << " solution" << ( (s==1) ? ' ' : 's' );
std::cout << std::endl << std::endl;
// Write the result to disk also
std::ofstream ofs(fname,std::ios::out);
std::set<std::pair<GameState,int> >::iterator it;
for (it=results.begin();it!=results.end();it++)
{
std::stringstream ss;
ss << "Solution " << i++ << ": Game starting in state ";
ss << it->first << " takes " << (it->second + 1) * 5;
ss << " minutes to complete";
ofs << ss.str() << std::endl;
std::cout << ss.str() << std::endl;
}
std::cout << std::endl;
}