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: | 824 / 1755 |
| Version history: | 36 change(s) |
| Referenced in: | [show references] |