1 | /////////////////////////// |
2 | // Your API to work with // |
3 | /////////////////////////// |
4 | |
5 | sinterface GameForAI { |
6 | L<Rect> submit(Pt point); // returns correct solution so AI can learn from it |
7 | } |
8 | |
9 | abstract sclass AI { |
10 | // stuff you get |
11 | |
12 | GameForAI game; |
13 | RGBImage image; |
14 | int steps; |
15 | bool visualize = true; |
16 | |
17 | RGBImage image() { ret image; } |
18 | int w() { ret image.w(); } |
19 | int h() { ret image.h(); } |
20 | L<Rect> submit(Pt p) { ret game.submit(p); } |
21 | L<Rect> submit(Rect r) { ret submit(middleOfRect(r)); } |
22 | L<Rect> submitNothing() { ret game.submit(null); } |
23 | |
24 | // implement this method and call submit() |
25 | abstract void go(); |
26 | |
27 | void done() {} |
28 | } |
29 | |
30 | /////////////// |
31 | // Main Game // |
32 | /////////////// |
33 | |
34 | static int w = 150, h = 150; |
35 | static int rounds = 1000; |
36 | static bool alwaysNewImage = true; // prevents AIs being stuck on an image |
37 | static double imageZoom = 2; |
38 | static int fps = 50; |
39 | static bool showBlunderWindow; |
40 | sS nonClickName = null; |
41 | |
42 | static int points, clicks, halfTime; |
43 | static ImageSurface is, isWrong; |
44 | static long isWrongLast; |
45 | static RGBImage img; |
46 | static new HashMap<Rect, Int> words; |
47 | static L<Rect> solution; |
48 | volatile sbool aiMode; |
49 | static JLabel lblScore, lblTiming, lblInstruction; |
50 | static new HashMap<Class, AI> ais; |
51 | static S winner; |
52 | |
53 | svoid pGame { |
54 | substance(); |
55 | swing { |
56 | thread { loadBinarySnippet(#1004373); } // preload applause animation |
57 | lblInstruction = jcenteredBoldLabel(); |
58 | nextImage(); |
59 | addToWindowTop(is, withMargin(lblInstruction)); |
60 | if (nempty(nonClickName)) |
61 | addToWindow(is, withTopMargin(jCenteredLine(jbutton(nonClickName, r { |
62 | userClicked(null) |
63 | })))); |
64 | addToWindow(is, withMargin(lblScore = jcenteredBoldLabel("Your score: 0 of 0"))); |
65 | for (final Class c : myNonAbstractClassesImplementing(AI)) { |
66 | final S name = shortClassName(c); |
67 | final JButton btn = jbutton(name); |
68 | onClick(btn, r { |
69 | if (aiMode) ret; |
70 | btn.setText(name + " Running..."); |
71 | thread { |
72 | Game game = testAI(c, voidfunc(Game game) { |
73 | if (game.step < 50 || (game.step % 10) == 0 || game.step == rounds) { |
74 | S text = name + " scored " + game.points + " of " + game.step; |
75 | if (game.step > halfTime) |
76 | text += " (" + (game.points-game.halfTimeScore) + "/" + (game.step-halfTime) + ")"; |
77 | if (game.error != null) |
78 | text += " - ERROR: " + game.error; |
79 | setText_noWait(btn, text); |
80 | } |
81 | }); |
82 | } |
83 | }); |
84 | addToWindow(is, withMargin(btn)); |
85 | } |
86 | addToWindow(is, lblTiming = jCenteredLabel(; |
87 | titlePopupMenuItem(is, "Show Winner Code", f showWinnerCode); |
88 | titlePopupMenuItem(is, "Restart AIs", r { clearMapWithCleanUp(ais); }); |
89 | titlePopupMenuItem(is, jCheckBoxMenuItem("Show Blunder Window", showBlunderWindow, r { showBlunderWindow = !showBlunderWindow; })); |
90 | addToWindow(is, withMargin(jbutton("Restart AIs", r { clearMapWithCleanUp(ais); }))); |
91 | |
92 | packFrame(is); |
93 | setFrameWidth(is, 400); |
94 | centerTopFrame(is); |
95 | hideConsole(); |
96 | } |
97 | } |
98 | |
99 | sclass Puzzle { |
100 | S instruction, nonClickName; |
101 | RGBImage image; |
102 | L<Rect> solution; |
103 | |
104 | *() {} |
105 | *(S *instruction, BufferedImage image, L<Rect> *solution) { |
106 | this.image = new RGBImage(image); |
107 | } |
108 | *(S *instruction, RGBImage *image, L<Rect> *solution) {} |
109 | *(S *instruction, S *nonClickName, RGBImage *image, L<Rect> *solution) {} |
110 | } |
111 | |
112 | svoid nextImage { |
113 | Puzzle puzzle = makePuzzle(); |
114 | setText(lblInstruction, or2(puzzle.instruction, "?")); |
115 | assertNotNull("Image is null", puzzle.image); |
116 | main.img = puzzle.image; |
117 | solution = puzzle.solution; |
118 | nonClickName = puzzle.nonClickName; |
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, imageZoom); |
126 | if (first) { |
127 | onLeftClick(is, voidfunc(Pt p) { userClicked(p) }); |
128 | disableImageSurfaceSelector(is); |
129 | |
130 | // animate if idle |
131 | awtEvery(is, 1000, r { |
132 | if (!aiMode && isInForeground(is) && !mouseInFrame(is)) |
133 | nextImage(); |
134 | }); |
135 | |
136 | // show current image occasionally when AI is running |
137 | awtEvery(is, 1000/fps, r { |
138 | if (aiMode) showTheImage(); |
139 | }); |
140 | } |
141 | } |
142 | |
143 | // AI stuff |
144 | |
145 | sclass Game implements GameForAI { |
146 | int points, step, halfTimeScore; |
147 | Pt submitted; |
148 | Throwable error; |
149 | |
150 | public L<Rect> submit(Pt p) { |
151 | if (submitted != null) fail("No multi-submit please"); |
152 | submitted = p == null ? nonPoint() : p; |
153 | bool correct = empty(solution) ? p == null |
154 | : p != null && anyRectContains(solution, p); |
155 | // print("p=" + p + ", solution=" + solution + ", correct=" + correct); |
156 | if (correct) { |
157 | ++points; |
158 | if (!alwaysNewImage) nextImage(); |
159 | } |
160 | ret solution; |
161 | } |
162 | } |
163 | |
164 | static Game scoreAI(final AI ai, O onScore) { |
165 | aiMode = true; |
166 | final long start = sysNow(); |
167 | try { |
168 | final new Game game; |
169 | setOpt(ai, +game); |
170 | halfTime = rounds/2; |
171 | while (game.step < rounds) { |
172 | if (game.step == halfTime) game.halfTimeScore = game.points; |
173 | ++game.step; |
174 | ++ai.steps; |
175 | game.submitted = null; |
176 | int points = game.points; |
177 | RGBImage image = img; |
178 | try { |
179 | setOpt(ai, +image); |
180 | ai.go(); |
181 | } catch e { |
182 | if (game.error == null) { // print first error to console |
183 | showConsole(); |
184 | printStackTrace(e); |
185 | } |
186 | game.error = e; |
187 | } finally { |
188 | setOpt(ai, image := null); |
189 | } |
190 | if (showBlunderWindow && points == game.points && hasElapsed(isWrongLast, 50)) { |
191 | isWrongLast = sysNow(); |
192 | bool first = isWrong == null; |
193 | S guess = ""; // game.submitted == null ? "no click" : game.submitted.x + "/" + game.submitted.y; |
194 | isWrong = showImage(isWrong, "Last Blunder" + (nempty(guess) ? " (" + guess + ")" : ""), rgbMarkPoint(rgbClone(image), game.submitted, Color.red, 3)); |
195 | if (first) { |
196 | setFrameWidth(isWrong, 230); |
197 | moveToTopRightCorner(isWrong); |
198 | moveFrameDown(isWrong, 100); |
199 | } |
200 | } |
201 | pcallF(onScore, game); |
202 | if (alwaysNewImage) nextImage(); |
203 | } |
204 | print("AI " + shortClassName(ai) + " points after " + rounds + " rounds: " + game.points); |
205 | ai.done(); |
206 | // We consider the game solved when an AI scores 100% in second half |
207 | if (game.points-game.halfTimeScore >= rounds-halfTime) thread { |
208 | titleStatus(is, "Solved!"); |
209 | showAnimationInTopLeftCorner(#1004373, game.points + " of " + rounds + " points!!", 3.0); |
210 | winner = structure(ai); |
211 | save("winner"); |
212 | } |
213 | ret game; |
214 | } finally { |
215 | aiMode = false; |
216 | awt { |
217 | lblTiming.setText(formatDouble(toSeconds(sysNow()-start), 1) + " s"); |
218 | nextImage(); |
219 | } |
220 | } |
221 | } |
222 | |
223 | static AI getAI(Class<? extends AI> c) { |
224 | AI ai = ais.get(c); |
225 | if (ai == null) ais.put(c, ai = nu(c)); |
226 | ret ai; |
227 | } |
228 | |
229 | // onScore: voidfunc(Game) |
230 | static Game testAI(Class<? extends AI> c, O onScore) { |
231 | ret scoreAI(getAI(c), onScore); |
232 | } |
233 | |
234 | svoid userClicked(Pt p) { |
235 | ++clicks; |
236 | bool correct = empty(solution) ? p == null |
237 | : p != null && anyRectContains(solution, is.pointFromComponentCoordinates(p)); |
238 | if (correct) { |
239 | ++points; |
240 | nextImage(); |
241 | } else if (alwaysNewImage) nextImage(); |
242 | lblScore.setText(print("Your score: " + points + " of " + clicks)); |
243 | } |
244 | |
245 | svoid showWinnerCode { |
246 | if (winner == null) load("winner"); |
247 | if (winner == null) messageBox("No winner yet"); |
248 | else showWrappedText("Winner Code - " + programTitle(), winner); |
249 | } |
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: | #1006891 |
Snippet name: | AI Game Where User Has To Click [Include] |
Eternal ID of this version: | #1006891/37 |
Text MD5: | 58950d720448af75ab2da549150245db |
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-08 17:09:26 |
Source code size: | 7702 bytes / 249 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 607 / 1527 |
Version history: | 36 change(s) |
Referenced in: | [show references] |