1 | /////////////////////////// |
2 | // Your API to work with // |
3 | /////////////////////////// |
4 | |
5 | sinterface GameForAI { |
6 | S submit(S text); // returns correct solution so AI can learn from it |
7 | bool compareSolutions(S a, S b); |
8 | } |
9 | |
10 | abstract sclass AI { |
11 | // stuff you get |
12 | |
13 | GameForAI game; |
14 | RGBImage image; |
15 | int steps; |
16 | bool visualize = true; |
17 | |
18 | RGBImage image() { ret image; } |
19 | BWImage bwImage() { ret new BWImage(image); } |
20 | int w() { ret image.w(); } |
21 | int h() { ret image.h(); } |
22 | S submit(S text) { ret game.submit(text); } |
23 | S submitNothing() { ret game.submit(null); } |
24 | bool compareSolutions(S a, S b) { ret game.compareSolutions(a, b); } |
25 | bool submitCorrectly(S text) { ret compareSolutions(text, submit(text)); } |
26 | |
27 | // implement this method and call submit() |
28 | abstract void go(); |
29 | |
30 | void done() {} |
31 | } |
32 | |
33 | /////////////// |
34 | // Main Game // |
35 | /////////////// |
36 | |
37 | static int rounds = 1000; |
38 | static bool alwaysNewImage = true; // prevents AIs being stuck on an image |
39 | static int fps = 50; |
40 | static bool showBlunderWindow = true; |
41 | static int blunderFPS = 20; |
42 | |
43 | static new Game userGame; |
44 | static int halfTime; |
45 | static ImageSurface is, isWrong; |
46 | static long isWrongLast; |
47 | static RGBImage img; |
48 | static S solution; |
49 | volatile sbool aiMode; |
50 | static JLabel lblScore, lblTiming, lblInstruction; |
51 | static new HashMap<Class, AI> ais; |
52 | static S winner; |
53 | static JTextField tfSolution; |
54 | |
55 | svoid pGame { |
56 | substance(); |
57 | swing { |
58 | thread { loadBinarySnippet(#1004373); } // preload applause animation |
59 | lblInstruction = jcenteredBoldLabel(); |
60 | nextImage(); |
61 | addToWindowTop(is, withMargin(lblInstruction)); |
62 | addToWindow(is, withTopMargin(tfSolution = jCenteredTextField())); |
63 | onEnter(tfSolution, r { |
64 | userSaid(getTextTrim(tfSolution)) |
65 | }); |
66 | addToWindow(is, withMargin(lblScore = jcenteredBoldLabel("Your score: 0 of 0"))); |
67 | for (final Class c : myNonAbstractClassesImplementing(AI)) { |
68 | final S name = shortClassName(c); |
69 | final JButton btn = jbutton(name); |
70 | onClick(btn, r { |
71 | if (aiMode) ret; |
72 | btn.setText(name + " Running..."); |
73 | thread { |
74 | Game game = testAI(c, voidfunc(Game game) { |
75 | if (game.step < 50 || (game.step % 10) == 0 || game.step == rounds) { |
76 | S text = name + " scored " + game.points + " of " + game.step; |
77 | if (game.step > halfTime) |
78 | text += " (" + (game.points-game.halfTimeScore) + "/" + (game.step-halfTime) + ")"; |
79 | if (game.error != null) |
80 | text += " - ERROR: " + game.error; |
81 | setText_noWait(btn, text); |
82 | } |
83 | }); |
84 | } |
85 | }); |
86 | addToWindow(is, withMargin(btn)); |
87 | } |
88 | addToWindow(is, lblTiming = jCenteredLabel(; |
89 | titlePopupMenuItem(is, "Show Winner Code", f showWinnerCode); |
90 | titlePopupMenuItem(is, "Restart AIs", r { clearMapWithCleanUp(ais); }); |
91 | titlePopupMenuItem(is, jCheckBoxMenuItem("Show Blunder Window", showBlunderWindow, r { showBlunderWindow = !showBlunderWindow; })); |
92 | addToWindow(is, withMargin(jbutton("Restart AIs", r { clearMapWithCleanUp(ais); }))); |
93 | |
94 | packFrame(is); |
95 | setFrameWidth(is, 400); |
96 | centerTopFrame(is); |
97 | hideConsole(); |
98 | } |
99 | } |
100 | |
101 | sclass Puzzle { |
102 | S instruction; |
103 | RGBImage image; |
104 | S solution; |
105 | |
106 | *() {} |
107 | *(S *instruction, BufferedImage image, S *solution) { |
108 | this.image = new RGBImage(image); |
109 | } |
110 | *(S *instruction, RGBImage *image, S *solution) {} |
111 | } |
112 | |
113 | svoid nextImage { |
114 | Puzzle puzzle = makePuzzle(); |
115 | setText(lblInstruction, or2(puzzle.instruction, "?")); |
116 | assertNotNull("Image is null", puzzle.image); |
117 | main.img = puzzle.image; |
118 | solution = puzzle.solution; |
119 | if (aiMode) ret; // occasional updates only when AI is running |
120 | showTheImage(); |
121 | } |
122 | |
123 | svoid showTheImage { |
124 | bool first = is == null; |
125 | is = showZoomedImage_centered(is, img, 2); |
126 | if (first) { |
127 | disableImageSurfaceSelector(is); |
128 | |
129 | // animate if idle |
130 | awtEvery(is, 1000, r { |
131 | if (!aiMode && isInForeground(is) && !mouseInFrame(is)) |
132 | nextImage(); |
133 | }); |
134 | |
135 | // show current image occasionally when AI is running |
136 | awtEvery(is, 1000/fps, r { |
137 | if (aiMode) showTheImage(); |
138 | }); |
139 | } |
140 | } |
141 | |
142 | // AI stuff |
143 | |
144 | sclass Game implements GameForAI { |
145 | int points, step, halfTimeScore; |
146 | S submitted; |
147 | Throwable error; |
148 | |
149 | public bool compareSolutions(S a, S b) { |
150 | ret eq(a, b); |
151 | } |
152 | |
153 | public S submit(S text) { |
154 | if (submitted != null) fail("No multi-submit please"); |
155 | submitted = unnull(text); |
156 | bool correct = compareSolutions(submitted, solution); |
157 | // print("p=" + p + ", solution=" + solution + ", correct=" + correct); |
158 | if (correct) { |
159 | ++points; |
160 | if (!alwaysNewImage) nextImage(); |
161 | } |
162 | ret solution; |
163 | } |
164 | } |
165 | |
166 | static Game scoreAI(final AI ai, O onScore) { |
167 | aiMode = true; |
168 | final long start = sysNow(); |
169 | try { |
170 | final new Game game; |
171 | setOpt(ai, +game); |
172 | halfTime = rounds/2; |
173 | while (game.step < rounds) { |
174 | if (game.step == halfTime) game.halfTimeScore = game.points; |
175 | ++game.step; |
176 | ++ai.steps; |
177 | game.submitted = null; |
178 | int points = game.points; |
179 | RGBImage image = img; |
180 | try { |
181 | setOpt(ai, +image); |
182 | ai.go(); |
183 | } catch e { |
184 | if (game.error == null) { // print first error to console |
185 | showConsole(); |
186 | printStackTrace(e); |
187 | } |
188 | game.error = e; |
189 | } finally { |
190 | setOpt(ai, image := null); |
191 | } |
192 | if (showBlunderWindow && points == game.points && hasElapsed(isWrongLast, 1000/blunderFPS)) { |
193 | isWrongLast = sysNow(); |
194 | bool first = isWrong == null; |
195 | S guess = game.submitted == null ? "-" : game.submitted; |
196 | isWrong = showImage(isWrong, "Last Blunder: " + guess, image); |
197 | if (first) { |
198 | setFrameWidth(isWrong, 230); |
199 | moveToTopRightCorner(isWrong); |
200 | moveFrameDown(isWrong, 100); |
201 | } |
202 | } |
203 | pcallF(onScore, game); |
204 | if (alwaysNewImage) nextImage(); |
205 | } |
206 | print("AI " + shortClassName(ai) + " points after " + rounds + " rounds: " + game.points); |
207 | ai.done(); |
208 | // We consider the game solved when an AI scores 100% in second half |
209 | if (game.points-game.halfTimeScore >= rounds-halfTime) thread { |
210 | titleStatus(is, "Solved!"); |
211 | showAnimationInTopLeftCorner(#1004373, game.points + " of " + rounds + " points!!", 3.0); |
212 | winner = structure(ai); |
213 | save("winner"); |
214 | } |
215 | ret game; |
216 | } finally { |
217 | aiMode = false; |
218 | awt { |
219 | lblTiming.setText(formatDouble(toSeconds(sysNow()-start), 1) + " s"); |
220 | nextImage(); |
221 | } |
222 | } |
223 | } |
224 | |
225 | static AI getAI(Class<? extends AI> c) { |
226 | AI ai = ais.get(c); |
227 | if (ai == null) ais.put(c, ai = nu(c)); |
228 | ret ai; |
229 | } |
230 | |
231 | // onScore: voidfunc(Game) |
232 | static Game testAI(Class<? extends AI> c, O onScore) { |
233 | ret scoreAI(getAI(c), onScore); |
234 | } |
235 | |
236 | svoid userSaid(S text) { |
237 | print("User said: " + text); |
238 | ++userGame.step; |
239 | userGame.submitted = null; |
240 | userGame.submit(text); |
241 | lblScore.setText(print("Your score: " + userGame.points + " of " + userGame.step)); |
242 | tfSolution.selectAll(); |
243 | if (alwaysNewImage) nextImage(); |
244 | } |
245 | |
246 | svoid showWinnerCode { |
247 | if (winner == null) load("winner"); |
248 | if (winner == null) messageBox("No winner yet"); |
249 | else showWrappedText("Winner Code - " + programTitle(), winner); |
250 | } |
Began life as a copy of #1006891
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1007130 |
Snippet name: | AI Game Where User Enters Text [Include] |
Eternal ID of this version: | #1007130/17 |
Text MD5: | 2463d16fcf595601e5eea0dfbf1daacb |
Author: | stefan |
Category: | javax / a.i. |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2017-03-06 14:23:47 |
Source code size: | 7446 bytes / 250 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 508 / 1041 |
Version history: | 16 change(s) |
Referenced in: | [show references] |