/////////////////////////// // Your API to work with // /////////////////////////// sinterface GameForAI { S submit(S text); // returns correct solution so AI can learn from it bool compareSolutions(S a, S b); } abstract sclass AI { // stuff you get GameForAI game; RGBImage image; int steps; bool visualize = true; RGBImage image() { ret image; } BWImage bwImage() { ret new BWImage(image); } int w() { ret image.w(); } int h() { ret image.h(); } S submit(S text) { ret game.submit(text); } S submitNothing() { ret game.submit(null); } bool compareSolutions(S a, S b) { ret game.compareSolutions(a, b); } bool submitCorrectly(S text) { ret compareSolutions(text, submit(text)); } // implement this method and call submit() abstract void go(); void done() {} } /////////////// // Main Game // /////////////// static int rounds = 1000; static bool alwaysNewImage = true; // prevents AIs being stuck on an image static int fps = 50; static bool showBlunderWindow = true; static int blunderFPS = 20; static new Game userGame; static int halfTime; static ImageSurface is, isWrong; static long isWrongLast; static RGBImage img; static S solution; volatile sbool aiMode; static JLabel lblScore, lblTiming, lblInstruction; static new HashMap ais; static S winner; static JTextField tfSolution; svoid pGame { substance(); swing { thread { loadBinarySnippet(#1004373); } // preload applause animation lblInstruction = jcenteredBoldLabel(); nextImage(); addToWindowTop(is, withMargin(lblInstruction)); addToWindow(is, withTopMargin(tfSolution = jCenteredTextField())); onEnter(tfSolution, r { userSaid(getTextTrim(tfSolution)) }); addToWindow(is, withMargin(lblScore = jcenteredBoldLabel("Your score: 0 of 0"))); for (final Class c : myNonAbstractClassesImplementing(AI)) { final S name = shortClassName(c); final JButton btn = jbutton(name); onClick(btn, r { if (aiMode) ret; btn.setText(name + " Running..."); thread { Game game = testAI(c, voidfunc(Game game) { if (game.step < 50 || (game.step % 10) == 0 || game.step == rounds) { S text = name + " scored " + game.points + " of " + game.step; if (game.step > halfTime) text += " (" + (game.points-game.halfTimeScore) + "/" + (game.step-halfTime) + ")"; if (game.error != null) text += " - ERROR: " + game.error; setText_noWait(btn, text); } }); } }); addToWindow(is, withMargin(btn)); } addToWindow(is, lblTiming = jCenteredLabel(; titlePopupMenuItem(is, "Show Winner Code", f showWinnerCode); titlePopupMenuItem(is, "Restart AIs", r { clearMapWithCleanUp(ais); }); titlePopupMenuItem(is, jCheckBoxMenuItem("Show Blunder Window", showBlunderWindow, r { showBlunderWindow = !showBlunderWindow; })); addToWindow(is, withMargin(jbutton("Restart AIs", r { clearMapWithCleanUp(ais); }))); packFrame(is); setFrameWidth(is, 400); centerTopFrame(is); hideConsole(); } } sclass Puzzle { S instruction; RGBImage image; S solution; *() {} *(S *instruction, BufferedImage image, S *solution) { this.image = new RGBImage(image); } *(S *instruction, RGBImage *image, S *solution) {} } svoid nextImage { Puzzle puzzle = makePuzzle(); setText(lblInstruction, or2(puzzle.instruction, "?")); assertNotNull("Image is null", puzzle.image); main.img = puzzle.image; solution = puzzle.solution; if (aiMode) ret; // occasional updates only when AI is running showTheImage(); } svoid showTheImage { bool first = is == null; is = showZoomedImage_centered(is, img, 2); if (first) { disableImageSurfaceSelector(is); // animate if idle awtEvery(is, 1000, r { if (!aiMode && isInForeground(is) && !mouseInFrame(is)) nextImage(); }); // show current image occasionally when AI is running awtEvery(is, 1000/fps, r { if (aiMode) showTheImage(); }); } } // AI stuff sclass Game implements GameForAI { int points, step, halfTimeScore; S submitted; Throwable error; public bool compareSolutions(S a, S b) { ret eq(a, b); } public S submit(S text) { if (submitted != null) fail("No multi-submit please"); submitted = unnull(text); bool correct = compareSolutions(submitted, solution); // print("p=" + p + ", solution=" + solution + ", correct=" + correct); if (correct) { ++points; if (!alwaysNewImage) nextImage(); } ret solution; } } static Game scoreAI(final AI ai, O onScore) { aiMode = true; final long start = sysNow(); try { final new Game game; setOpt(ai, +game); halfTime = rounds/2; while (game.step < rounds) { if (game.step == halfTime) game.halfTimeScore = game.points; ++game.step; ++ai.steps; game.submitted = null; int points = game.points; RGBImage image = img; try { setOpt(ai, +image); ai.go(); } catch e { if (game.error == null) { // print first error to console showConsole(); printStackTrace(e); } game.error = e; } finally { setOpt(ai, image := null); } if (showBlunderWindow && points == game.points && hasElapsed(isWrongLast, 1000/blunderFPS)) { isWrongLast = sysNow(); bool first = isWrong == null; S guess = game.submitted == null ? "-" : game.submitted; isWrong = showImage(isWrong, "Last Blunder: " + guess, image); if (first) { setFrameWidth(isWrong, 230); moveToTopRightCorner(isWrong); moveFrameDown(isWrong, 100); } } pcallF(onScore, game); if (alwaysNewImage) nextImage(); } print("AI " + shortClassName(ai) + " points after " + rounds + " rounds: " + game.points); ai.done(); // We consider the game solved when an AI scores 100% in second half if (game.points-game.halfTimeScore >= rounds-halfTime) thread { titleStatus(is, "Solved!"); showAnimationInTopLeftCorner(#1004373, game.points + " of " + rounds + " points!!", 3.0); winner = structure(ai); save("winner"); } ret game; } finally { aiMode = false; awt { lblTiming.setText(formatDouble(toSeconds(sysNow()-start), 1) + " s"); nextImage(); } } } static AI getAI(Class c) { AI ai = ais.get(c); if (ai == null) ais.put(c, ai = nu(c)); ret ai; } // onScore: voidfunc(Game) static Game testAI(Class c, O onScore) { ret scoreAI(getAI(c), onScore); } svoid userSaid(S text) { print("User said: " + text); ++userGame.step; userGame.submitted = null; userGame.submit(text); lblScore.setText(print("Your score: " + userGame.points + " of " + userGame.step)); tfSolution.selectAll(); if (alwaysNewImage) nextImage(); } svoid showWinnerCode { if (winner == null) load("winner"); if (winner == null) messageBox("No winner yet"); else showWrappedText("Winner Code - " + programTitle(), winner); }