Uses 3874K of libraries. Click here for Pure Java version (6909L/49K/174K).
1 | !7 |
2 | |
3 | /////////////////////////// |
4 | // Your API to work with // |
5 | /////////////////////////// |
6 | |
7 | abstract sclass GameForAI { |
8 | abstract RGBImage getImage(); |
9 | abstract L<Rect> submit(Pt point); // returns correct solution so AI can learn from it |
10 | } |
11 | |
12 | abstract sclass AI { |
13 | // stuff you get |
14 | |
15 | GameForAI game; |
16 | RGBImage image; |
17 | int steps; |
18 | RGBImage image() { ret image; } |
19 | int w() { ret image.w(); } |
20 | int h() { ret image.h(); } |
21 | L<Rect> submit(Pt p) { ret game.submit(p); } |
22 | L<Rect> submit(Rect r) { ret submit(middleOfRect(r)); } |
23 | L<Rect> submitNothing() { ret game.submit(null); } |
24 | |
25 | // implement this method and call submit() |
26 | abstract void go(); |
27 | |
28 | void done() {} |
29 | } |
30 | |
31 | ////////////////////////////////////// |
32 | // Test AIs. Just add your own here // |
33 | ////////////////////////////////////// |
34 | |
35 | AI > ClickAnyNumber { |
36 | void go { |
37 | submit(random(segment(image; |
38 | } |
39 | } |
40 | |
41 | static ImageSurface isFound; |
42 | |
43 | AI > FindPairs { |
44 | void go { |
45 | L<Rect> solution = submitNothing(); |
46 | if (l(solution) == 2) |
47 | isFound = setFrameWidth(showImage(isFound, "Pairs!", mergeImagesHorizontally(rgbClips(image, solution))), 200); |
48 | } |
49 | } |
50 | |
51 | AI > FindNonPairs { |
52 | void go { |
53 | L<Rect> solution = submitNothing(); |
54 | if (l(solution) == 1) { |
55 | L<Rect> myRects = segment(image); |
56 | Rect sol = autoCropOfRGBImage(image, first(solution)); |
57 | myRects = dropRectsIntersectingWith(myRects, sol); |
58 | if (l(myRects) == 1) |
59 | isFound = setFrameWidth(showImage(isFound, "Non-Pairs!", mergeImagesHorizontally(rgbClips(image, concatLists(ll(sol), myRects)))), 250); |
60 | } |
61 | } |
62 | } |
63 | |
64 | AI > JARVIS { |
65 | new BWImageCategorizer cat; |
66 | bool firstRun = true; |
67 | static ImageSurface is; |
68 | |
69 | BWImage simplifyImage(RGBImage img) { |
70 | ret new Image2B(img).toBW(); |
71 | } |
72 | |
73 | Rect guess() { |
74 | L<Rect> myRects = segment(image); |
75 | if (l(myRects) != 2) null; |
76 | int i = cat.locateImage(simplifyImage(image.clip(first(myRects; |
77 | int j = cat.locateImage(simplifyImage(image.clip(second(myRects; |
78 | ret get(myRects, i < j ? 0 : 1); |
79 | } |
80 | |
81 | void go { |
82 | L<Rect> solution = submit(guess()); |
83 | Analysis analysis = analyzePair(image, solution); |
84 | if (analysis == null) ret; |
85 | int n = cat.numCategories(); |
86 | int i = cat.addImage(simplifyImage(first(analysis.images))); |
87 | int j = cat.addImage(simplifyImage(second(analysis.images))); |
88 | bool update = cat.numCategories() > n; |
89 | if (analysis.hetero && i > j) { |
90 | swap(cat.images, i, j); |
91 | update = true; |
92 | } |
93 | if (cat.numCategories() > 10) { |
94 | cat.allowedDistance += 0.01f; |
95 | print("Got 11 categories after " + steps + " rounds. Upping allowed distance to: " + cat.allowedDistance); |
96 | cat.clear(); |
97 | steps = 0; |
98 | } else if (update) { |
99 | bool first = is == null; |
100 | is = showImage_centered(is, "JARVIS Has Learned These Numbers", mergeImagesHorizontally(cat.rgbImages())); |
101 | if (first) { |
102 | setFrameWidth(is, 400); |
103 | moveToTopRightCorner(getFrame(is)); |
104 | } |
105 | } |
106 | if (firstRun && steps < 50) sleep(blend(1000, 0, steps/50.0)); |
107 | } |
108 | |
109 | void done() { |
110 | print("Categories: " + cat.numCategories() + " (should probably be 10)"); |
111 | steps = 0; firstRun = false; |
112 | } |
113 | |
114 | void cleanMeUp() { |
115 | print("Jarvis cleaning up"); |
116 | if (is != null) is.setImage((BufferedImage) null); |
117 | } |
118 | } |
119 | |
120 | sclass Analysis { |
121 | bool hetero; |
122 | L<RGBImage> images; |
123 | |
124 | *() {} |
125 | *(bool *hetero, L<RGBImage> *images) {} |
126 | } |
127 | |
128 | static Analysis analyzePair(RGBImage image, L<Rect> solution) { |
129 | if (l(solution) == 2) |
130 | ret new Analysis(false, rgbClips(image, rgbAutoCropRects(image, solution))); |
131 | if (l(solution) == 1) { |
132 | L<Rect> myRects = segment(image); |
133 | Rect sol = autoCropOfRGBImage(image, first(solution)); |
134 | myRects = dropRectsIntersectingWith(myRects, sol); |
135 | if (l(myRects) == 1) |
136 | ret new Analysis(true, rgbClips(image, concatLists(ll(sol), myRects))); |
137 | } |
138 | null; // no insights today |
139 | } |
140 | |
141 | static L<Rect> segment(RGBImage img) { |
142 | ret autoSegment(new BWImage(img), 3); // 2 is too small |
143 | } |
144 | |
145 | /////////////// |
146 | // Main Game // |
147 | /////////////// |
148 | |
149 | static int w = 150, h = 150; |
150 | static int border = 10, fontSize = 30, numberMargin = 3; |
151 | static int rounds = 1000, numbers = 2; |
152 | static S font = #1004887; |
153 | static bool alwaysNewImage = true; // prevents AIs being stuck on an image |
154 | static int fps = 50; |
155 | |
156 | static int points, clicks; |
157 | static ImageSurface is, isWrong; |
158 | static long isWrongLast; |
159 | static RGBImage img; |
160 | static new HashMap<Rect, Int> words; |
161 | static L<Rect> solution; |
162 | volatile sbool aiMode; |
163 | static JLabel lblScore, lblTiming; |
164 | static new HashMap<Class, AI> ais; |
165 | |
166 | p-substance { |
167 | thread { loadBinarySnippet(#1004373); } // preload applause animation |
168 | nextImage(); |
169 | addToWindow(is, withMargin(lblScore = jcenteredBoldLabel("Your score: 0 of 0"))); |
170 | for (final Class c : myNonAbstractClassesImplementing(AI)) { |
171 | final S name = shortClassName(c); |
172 | final JButton btn = jbutton(name); |
173 | onClick(btn, r { |
174 | if (aiMode) ret; |
175 | btn.setText(name + " Running..."); |
176 | thread { |
177 | Game game = testAI(c, voidfunc(Game game) { |
178 | if (game.step < 50 || (game.step % 10) == 0 || game.step == rounds) { |
179 | S text = name + " scored " + game.points + " of " + game.step; |
180 | if (game.error != null) |
181 | text += " - ERROR: " + game.error; |
182 | setText_noWait(btn, text); |
183 | } |
184 | }); |
185 | } |
186 | }); |
187 | addToWindow(is, withMargin(btn)); |
188 | } |
189 | addToWindow(is, lblTiming = jCenteredLabel(; |
190 | titlePopupMenuItem(is, "Restart AIs", r { clearMapWithCleanUp(ais); }); |
191 | addToWindow(is, withMargin(jbutton("Restart AIs", r { clearMapWithCleanUp(ais); }))); |
192 | |
193 | packFrame(is); |
194 | setFrameWidth(is, 400); |
195 | centerTopFrame(is); |
196 | hideConsole(); |
197 | } |
198 | |
199 | svoid nextImage { |
200 | RGBImage img = rgbImage(Color.white, w, h); |
201 | words.clear(); |
202 | new L<Rect> taken; |
203 | for i to numbers: { |
204 | int num = random(10); |
205 | S s = str(num); |
206 | RGB color = new RGB(random(0.5), random(0.5), random(0.5)); |
207 | renderText_fg.set(color.getColor()); |
208 | BufferedImage ti = renderText(font, fontSize, s); |
209 | Rect r; |
210 | int safety = 0; |
211 | do { |
212 | r = randomRect(w, h, border, ti.getWidth(), ti.getHeight()); |
213 | if (r == null || ++safety >= 1000) fail("Image too small: \*ti.getWidth()*/*\*ti.getHeight()*/ > \*w*/*\*h*/"); |
214 | } while (anyRectOverlaps(taken, r)); |
215 | rgbCopy(ti, img, r.x, r.y); |
216 | words.put(r, num); |
217 | taken.add(growRect(r, numberMargin)); |
218 | } |
219 | solution = keysWithBiggestValue(words); |
220 | |
221 | main.img = img; |
222 | if (aiMode) ret; // occasional updates only when AI is running |
223 | showTheImage(); |
224 | } |
225 | |
226 | svoid showTheImage { |
227 | bool first = is == null; |
228 | is = showZoomedImage_centered(is, img, "Click On The Highest Number!", 1.5); |
229 | if (first) { |
230 | onLeftClick(is, voidfunc(Pt p) { |
231 | ++clicks; |
232 | if (anyRectContains(solution, is.pointFromComponentCoordinates(p))) { |
233 | ++points; |
234 | nextImage(); |
235 | } else if (alwaysNewImage) nextImage(); |
236 | lblScore.setText(print("Your score: " + points + " of " + clicks)); |
237 | }); |
238 | disableImageSurfaceSelector(is); |
239 | |
240 | // animate if idle |
241 | awtEvery(is, 1000, r { |
242 | if (!aiMode && isInForeground(is) && !mouseInComponent(is)) |
243 | nextImage(); |
244 | }); |
245 | |
246 | // show current image occasionally when AI is running |
247 | awtEvery(is, 1000/fps, r { |
248 | if (aiMode) showTheImage(); |
249 | }); |
250 | } |
251 | } |
252 | |
253 | // AI stuff |
254 | |
255 | sclass Game extends GameForAI { |
256 | int points, step; |
257 | Pt submitted; |
258 | Throwable error; |
259 | |
260 | RGBImage getImage() { ret img; } |
261 | |
262 | L<Rect> submit(Pt p) { |
263 | if (submitted != null) fail("No multi-submit please"); |
264 | submitted = p == null ? new Pt(-9, -9) : p; |
265 | if (p != null && anyRectContains(solution, p)) { |
266 | ++points; |
267 | if (!alwaysNewImage) nextImage(); |
268 | } |
269 | ret solution; |
270 | } |
271 | } |
272 | |
273 | static Game scoreAI(AI ai, O onScore) { |
274 | aiMode = true; |
275 | final long start = sysNow(); |
276 | try { |
277 | final new Game game; |
278 | setOpt(ai, +game); |
279 | while (game.step < rounds) { |
280 | ++game.step; |
281 | ++ai.steps; |
282 | game.submitted = null; |
283 | int points = game.points; |
284 | RGBImage image = img; |
285 | try { |
286 | setOpt(ai, +image); |
287 | ai.go(); |
288 | } catch e { |
289 | if (game.error == null) { // print first error to console |
290 | showConsole(); |
291 | printStackTrace(e); |
292 | } |
293 | game.error = e; |
294 | } finally { |
295 | setOpt(ai, image := null); |
296 | } |
297 | if (points == game.points && hasElapsed(isWrongLast, 50)) { |
298 | isWrongLast = sysNow(); |
299 | bool first = isWrong == null; |
300 | isWrong = showImage(isWrong, "Last Blunder", rgbMarkPoint(image, game.submitted, Color.red, 3)); |
301 | if (first) { |
302 | setFrameWidth(isWrong, 230); |
303 | moveToTopRightCorner(isWrong); |
304 | moveFrameDown(isWrong, 100); |
305 | } |
306 | } |
307 | pcallF(onScore, game); |
308 | if (alwaysNewImage) nextImage(); |
309 | } |
310 | print("AI " + shortClassName(ai) + " points after " + rounds + " rounds: " + game.points); |
311 | ai.done(); |
312 | if (game.points >= rounds) thread { showAnimationInTopLeftCorner(#1004373, game.points + " of " + rounds + " points!!", 3.0); } |
313 | ret game; |
314 | } finally { |
315 | aiMode = false; |
316 | awt { |
317 | lblTiming.setText(formatDouble(toSeconds(sysNow()-start), 1) + " s"); |
318 | nextImage(); |
319 | } |
320 | } |
321 | } |
322 | |
323 | static AI getAI(Class<? extends AI> c) { |
324 | AI ai = ais.get(c); |
325 | if (ai == null) ais.put(c, ai = nu(c)); |
326 | ret ai; |
327 | } |
328 | |
329 | // onScore: voidfunc(Game) |
330 | static Game testAI(Class<? extends AI> c, O onScore) { |
331 | ret scoreAI(getAI(c), onScore); |
332 | } |
Began life as a copy of #1006752
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: | #1006753 |
Snippet name: | Second A. I. Game Simplified & Solved |
Eternal ID of this version: | #1006753/130 |
Text MD5: | 2e737c0b59655989c054e0c2444c7af2 |
Transpilation MD5: | 8120804f7ea57b2990fbcfd34691461d |
Author: | stefan |
Category: | javax / gui |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2017-02-03 23:58:17 |
Source code size: | 9723 bytes / 332 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 724 / 1199 |
Version history: | 129 change(s) |
Referenced in: | [show references] |