!747 !pcall { m { p { makeAndroid3("Master Bot."); update(); } static class Bot { int port; S helloString; S botID; L questions; } static new L bots; static synchronized S answer(S s, L history) { new Matches m; if ("!update".equalsIgnoreCase(s)) { update(); ret "OK. " + stats(); } if (match3("search *", s, m)) { //search(s, 3); // TODO } ret search(s); } static void update() { L list = quickBotScan(); new L bots; for (ProgramScan.Program p : list) pcall { new Bot bot; bot.port = p.port; bot.helloString = p.helloString; bot.botID = getProgramIDOfBot(bot.port); bot.questions = getSupportedQuestions(bot.botID); print(bot.questions.size() + " question(s) found in bot " + quote(bot.helloString)); bots.add(bot); } synchronized(main.class) { main.bots = bots; } //print("Bots found: " + structure(bots)); print(stats()); } static synchronized S stats(){ ret bots.size() + " bots, " + countQuestions() + " questions."; } static synchronized int countQuestions() { int n = 0; for (Bot bot : bots) n += bot.questions.size(); return n; } static S getProgramIDOfBot(int port) { S answer = sendToLocalBot(port, "what is your program id"); if (!isSnippetID(answer)) fail("huch: " + answer); return formatSnippetID(answer); } static class BQ { Bot bot; S question; *(Bot *bot, S *question) {} *() {} } static S search(S input) { new HashMap scores; for (Bot bot : bots) { for (S question : bot.questions) { long score = scoreQuestion(input, question); scores.put(new BQ(bot, question), score); } } BQ bq = getHighest(scores); return bq == null ? "No match." : format3("Best match: * *", bq.bot.botID, bq.question); } static long scoreQuestion(S input, S question) { return commonPrefix(input.toUpperCase(), question.toUpperCase()).length(); } static BQ getHighest(Map map) { BQ best = null; long bestScore = 0; for (Map.Entry entry : map.entrySet()) if (best == null || entry.getValue() > bestScore) { best = entry.getKey(); bestScore = entry.getValue(); } return best; } }