Activate cheating
[troll.git] / src / main / java / com / codingame / game / View.java
1 package com.codingame.game;
2
3 import java.util.Random;
4 import java.util.ArrayList;
5 import java.util.Comparator;
6
7 import com.codingame.gameengine.core.GameManager;
8 import com.codingame.gameengine.core.MultiplayerGameManager;
9 import com.codingame.gameengine.module.entities.GraphicEntityModule;
10 import com.codingame.gameengine.module.entities.Rectangle;
11 import com.codingame.gameengine.module.entities.Sprite;
12 import com.codingame.gameengine.module.entities.SpriteAnimation;
13 import com.codingame.gameengine.module.entities.Text;
14 import com.codingame.gameengine.module.entities.TextBasedEntity;
15 import com.codingame.gameengine.module.entities.Group;
16 import com.codingame.gameengine.module.entities.Curve;
17 import com.google.inject.Inject;
18
19 class View {
20     @Inject private MultiplayerGameManager<com.codingame.game.Player> gameManager;
21     @Inject private GraphicEntityModule graphicEntityModule;
22     @Inject PantsModule pantsModule;
23
24     enum Dir {
25         LEFT("walks left.", 0),
26         STILL("stands still.", 1),
27         RIGHT("walks right.", 2);
28
29         String movement; int index;
30         Dir(String mvt, int i) { movement = mvt; index = i; }
31     }
32
33     class Player {
34         Model.Player model;
35
36         int colorToken;
37         String nicknameToken;
38         String avatarToken;
39
40         double frameRot;
41
42         Group avatar;
43         Text stoneCounter;
44         Text message;
45         Sprite castle;
46         Text stone;
47         Text stoneReminder;
48
49         void init(com.codingame.game.Player p) {
50             model = p.model;
51             colorToken = p.getColorToken();
52             nicknameToken = p.getNicknameToken();
53             avatarToken = p.getAvatarToken();
54
55             boolean p0 = model.index == 0;
56             int x = p0 ? 280 : 1920 - 280;
57             int y = 220;
58
59             Sprite frame = graphicEntityModule.createSprite()
60                 .setImage("frame.png")
61                 .setAnchor(0.5)
62                 .setRotation(frameRot)
63                 .setZIndex(22)
64                 .setTint(colorToken);
65
66             Sprite frameBg = graphicEntityModule.createSprite()
67                 .setImage("frame_bg.png")
68                 .setAnchor(0.5)
69                 .setRotation(frameRot)
70                 .setZIndex(20);
71
72             Sprite avatarSprite = graphicEntityModule.createSprite()
73                 .setZIndex(21)
74                 .setImage(avatarToken)
75                 .setAnchor(0.5)
76                 .setBaseHeight(116)
77                 .setBaseWidth(116);
78
79             avatar = graphicEntityModule
80                 .createGroup(frame, frameBg, avatarSprite)
81                 .setX(x).setY(y);
82
83             Text text = graphicEntityModule.createText(nicknameToken)
84                     .setX(x)
85                     .setY(y + 120)
86                     .setZIndex(20)
87                     .setFontSize(40)
88                     .setFillColor(0x7f3f00)
89                     .setAnchor(0.5);
90
91             stoneCounter = graphicEntityModule.createText()
92                 .setX(x)
93                 .setY(y+200)
94                 .setZIndex(20)
95                 .setFontSize(40)
96                 .setFillColor(0x7f3f00)
97                 .setAnchor(0.5);
98             updateStoneCounter();
99
100             message = graphicEntityModule.createText()
101                 .setX(p0 ? 15 : 1920-15)
102                 .setY(680)
103                 .setZIndex(1)
104                 .setFontSize(40)
105                 .setFillColor(0xffbf7f)
106                 .setAnchorX(p0 ? 0 : 1)
107                 .setAnchorY(1);
108
109             castle = graphicEntityModule.createSprite()
110                 .setImage("castle.png")
111                 .setTint(colorToken)
112                 .setX(p0 ? 160 : 1920-160)
113                 .setY(p0 ? 890 : 880)
114                 .setZIndex(1)
115                 .setAnchorX(0.5)
116                 .setAnchorY(1)
117                 .setScaleX(p0 ? 1 : -1);
118
119             stone = graphicEntityModule.createText()
120                 .setZIndex(3)
121                 .setFontSize(150)
122                 .setFillColor(0x12322a)
123                 .setAnchor(0.5)
124                 .setAlpha(0);
125
126             stoneReminder = graphicEntityModule.createText()
127                 .setX(p0 ? x + 100 : x - 100)
128                 .setY(y)
129                 .setZIndex(20)
130                 .setFontSize(80)
131                 .setFontFamily("monospace")
132                 .setStrokeColor(0xff0080)
133                 .setFillColor(0xff0080)
134                 .setAnchorX(p0 ? 0 : 1)
135                 .setAnchorY(0.5);
136             pantsModule.displayOnToggleState(stoneReminder, "debug", true);
137         }
138
139         void updateStoneCounter() {
140             int stones = model.getStones();
141             if (stones <= 0) {
142                 stoneCounter.setText("Out of stones!");
143                 stoneCounter.setFillColor(0xff7777);
144             }
145             else if (stones == 1) {
146                 stoneCounter.setText("1 stone");
147                 stoneCounter.setFillColor(0xffbb77);
148             }
149             else {
150                 stoneCounter.setText(stones + " stones");
151             }
152         }
153
154         void animateStones(int stones) {
155             String stonesString = Integer.valueOf(stones).toString();
156             stone.setX(castle.getX());
157             stone.setY(castle.getY() - 100);
158             stone.setText(stonesString);
159             stone.setAlpha(1);
160             graphicEntityModule.commitEntityState(0, stone);
161     
162             int peakX = (castle.getX() + troll.getX()) / 2;
163             int peakY = 540;
164             stone.setX(peakX);
165             stone.setY(peakY, Curve.EASE_OUT);
166             graphicEntityModule.commitEntityState(0.25,
167                                                   stone,
168                                                   stoneCounter);
169     
170             stone.setX(troll.getX());
171             stone.setY(troll.getY() - 50, Curve.EASE_IN);
172             stone.setAlpha(0, Curve.EASE_IN);
173             graphicEntityModule.commitEntityState(0.5, stone);
174
175             stoneReminder.setText(stonesString);
176             graphicEntityModule.commitEntityState(0, stoneReminder);
177         }
178
179         void displayMessage(String msg) {
180             message.setText(msg);
181             graphicEntityModule.commitEntityState(0, message);
182         }
183
184         void markLoser() {
185             graphicEntityModule.commitEntityState(0.5, avatar);
186             int dir = random.nextInt(2) == 1 ? 1 : -1;
187             avatar.setRotation(dir * 170 * Math.PI / 180, Curve.ELASTIC);
188         }
189
190         void destroy() {
191             gameManager.addToGameSummary(GameManager.formatErrorMessage(trollRace.starter + " destroys " + nicknameToken + "."));
192             graphicEntityModule.commitEntityState(0.5, castle);
193             castle.setX(castle.getX(), Curve.ELASTIC);
194             castle.setScaleY(-0.2, Curve.EASE_IN);
195         }
196
197         void startTurn() {
198             graphicEntityModule.commitEntityState(0, stoneReminder);
199         }
200
201         void victory() {
202             gameManager.addToGameSummary(GameManager.formatSuccessMessage(nicknameToken + " wins."));
203             graphicEntityModule.commitEntityState(0.5, avatar);
204             avatar.setScaleX(1.5, Curve.EASE_OUT);
205             avatar.setScaleY(1.5, Curve.EASE_OUT);
206             avatar.setRotation((random.nextDouble() - 0.5) * Math.PI / 18,
207                                Curve.ELASTIC);
208         }
209
210         void throwStones(int stones) {
211             gameManager.addToGameSummary(String.format("%s throws %d stone%s at " + trollRace.nonStarter(), nicknameToken, stones, stones == 1 ? "" : "s"));
212         }
213
214         void threwMoreStonesThanHad() {
215             gameManager.addToGameSummary(GameManager.formatErrorMessage(nicknameToken + " tried to throw more stones than they had.  I'll let it slide for this time.  (But not let them throw that much!)"));
216         }
217
218         void failedToThrowStonesAndShouldHave() {
219             gameManager.addToGameSummary(GameManager.formatErrorMessage(nicknameToken + " tried not throwing any stones.  Fixing that for them because I'm in a good mood today."));
220         }
221
222         void markTimeout() {
223             animateLoss(avatar.getX(), avatar.getY(), 100, "SLOW\nPOKE");
224         }
225
226         void markIllegal() {
227             animateLoss(avatar.getX(), avatar.getY(), 100, "STUPID");
228         }
229
230         void markCheat() {
231             animateLoss(avatar.getX(), avatar.getY(), 100, "CHEATER");
232         }
233     }
234
235     Model model;
236     Random random = new Random();
237     Text trollMessage;
238     Group troll;
239     Text trollPositionGauge;
240     Player p0 = new Player(), p1 = new Player();
241     Text turnCounter; int _turns = 0;
242
243     void init(Model m) {
244         model = m;
245         drawBackground();
246
247         /*
248          * Random π/2-grained rotation of the avatar frames.  Avoid
249          * having them π/2 apart, though, as one of them is likely
250          * going to end upside-down and the trick would be revealed.
251          * And I'd have to "draw" a new frame. Ewww.
252          */
253         p0.frameRot = random.nextInt(4) * Math.PI / 2;
254         p0.init(gameManager.getPlayer(0));
255         p1.frameRot = p1.frameRot +
256             (random.nextInt(2) == 1 ? 1 : -1) * Math.PI / 2;
257         p1.init(gameManager.getPlayer(1));
258
259         drawTroll();
260
261         drawDebug();
262     }
263
264     void startTurn() {
265         p0.startTurn();
266         p1.startTurn();
267
268         trollMessage.setX(troll.getX());
269
270         animateTurnCounter();
271     }
272
273     private class Pos {
274         int x, y;
275         Pos(int _x, int _y) { x = _x; y = _y; }
276     }
277
278     private void drawBackground() {
279         graphicEntityModule.createSprite()
280                 .setImage("background.png")
281                 .setAnchor(0);
282
283         int numMountains = random.nextInt(5);
284         while (numMountains --> 0) {
285             final int pngWidth = 366;
286             double scale = 0.5 * (1 + random.nextDouble());
287             int x = random.nextInt(1920 + (int) (scale*pngWidth))
288                 - (int) (scale*pngWidth/2);
289             int baseTint = 64 + random.nextInt(128);
290             Sprite mountain = graphicEntityModule.createSprite()
291                 .setImage("mountain.png")
292                 .setX(x)
293                 .setY(680)
294                 .setAnchorX(0.5)
295                 .setAnchorY(283.0 / 321.0)
296                 .setRotation((random.nextDouble() - 0.5) * Math.PI / 1800)
297                 .setScaleX(random.nextInt(2) == 0 ? scale : -scale)
298                 .setScaleY(scale * (1 + (random.nextDouble() - 0.5) / 2))
299                 .setSkewX((random.nextDouble() - 0.5) / 4)
300                 .setSkewY((random.nextDouble() - 0.5) / 8)
301                 .setTint((baseTint + random.nextInt(16) - 8) * 0x010000
302                          + (baseTint + random.nextInt(16) - 8) * 0x0100
303                          + (baseTint + random.nextInt(16) - 8) * 0x01);
304             graphicEntityModule.createSprite().setImage("mountaintop.png")
305                 .setX(mountain.getX())
306                 .setY(mountain.getY())
307                 .setAnchorX(mountain.getAnchorX())
308                 .setAnchorY(mountain.getAnchorY())
309                 .setRotation(mountain.getRotation())
310                 .setScaleX(mountain.getScaleX())
311                 .setScaleY(mountain.getScaleY())
312                 .setSkewX(mountain.getSkewX())
313                 .setSkewY(mountain.getSkewY());
314         }
315
316         int numTrees = random.nextInt(21);
317         ArrayList<Pos> poss = new ArrayList<Pos>(numTrees);
318         while (numTrees --> 0) {
319             int x, y;
320             do {
321                 x = random.nextInt(1920);
322                 // yes, this biases randomness wrt perspective! :-(
323                 y = 700 + random.nextInt(175);
324             } while (y > 880 && (x < 200 || x > 1720));
325             poss.add(new Pos(x, y));
326         }
327         poss.sort(new Comparator<Pos>() {
328                 public int compare(Pos a, Pos b) { return a.y < b.y ? -1 : 1; }
329             });
330
331         for (Pos p : poss) {
332             double scale = ( 90.0 / 433.0           // base height from PNG
333                              * (p.y - 680) / (875 - 680) ); // perspective
334             graphicEntityModule.createSprite()
335                 .setImage(random.nextInt(2) == 0 ? "Alshockv1.png"
336                                                  : "Alshockv2.png")
337                 .setAnchorX(0.5)
338                 .setAnchorY(1)
339                 .setX(p.x)
340                 .setY(p.y)
341                 .setScaleX(scale * (random.nextInt(2) == 0 ? -1 : 1)
342                            * (1 + (random.nextDouble() - 0.5) / 6))
343                 .setScaleY(scale * (1 + (random.nextDouble() -0.5) / 6))
344                 .setRotation((random.nextDouble() - 0.5) * Math.PI / 1800)
345                 .setSkewX((random.nextDouble() - 0.5) /4)
346                 .setSkewY((random.nextDouble() - 0.5) /8);
347         }
348
349         // base png: 514×387
350         Sprite f7u12 = graphicEntityModule.createSprite()
351             .setImage("f7u12.png")
352             .setX(1920 / 2)
353             .setY(1080 / 2)
354             .setAnchorX(0.5)
355             .setAnchorY(0.5)
356             .setBaseWidth(514*1080/387)
357             .setBaseHeight(1080)
358             .setZIndex(200);
359         pantsModule.displayOnToggleState(f7u12, "troll", true);
360     }
361
362     enum TrollRace {
363         Troll("The troll", 0xfac200, "bland"),
364         IceTroll("The ice troll", 0x59a2a2, "ice"),
365         RockTroll("The rock troll", 0x78877f, "rock"),
366         WaterTroll("The water troll", 0x2b2fc6, "water"),
367         OlogHai("The Olog-Hai", 0x5b2e7d, "ologhai");
368         String starter, parser; int tint;
369         TrollRace(String s, int t, String p) {
370             starter = s;
371             tint = t;
372             parser = p;
373         }
374         String nonStarter() {
375             return Character.toLowerCase(starter.charAt(0))
376                 + starter.substring(1);
377         }
378     }
379     TrollRace trollRace;
380
381     private void drawTroll() {
382         int r, league = gameManager.getLeagueLevel();
383         if (league <= 1) r = 4;
384         else if (league <= 2) r = 8;
385         else r = 10;
386
387         r = random.nextInt(r);
388         if (r < 4) trollRace = TrollRace.Troll;
389         else if (r < 6) trollRace = TrollRace.IceTroll;
390         else if (r < 8) trollRace = TrollRace.RockTroll;
391         else if (r < 9) trollRace = TrollRace.WaterTroll;
392         else if (r < 10) trollRace = TrollRace.OlogHai;
393         else throw new RuntimeException("Internal error: unknown troll race " + r);
394
395         // We read it for debugging purposes, but don't echo it back
396         // to the IDE.  It is, after all, *not* a map parameter!
397         String buf = gameManager.getGameParameters().getProperty("ehtnicity");
398         if (buf != null) {
399             String key = "";
400             for (char c : buf.toCharArray())
401                 if (Character.isLetter(c))
402                     key += Character.toLowerCase(c);
403             iHateJava: do {
404                 for (TrollRace race : TrollRace.values()) {
405                     if (key.equals(race.parser)) {
406                         trollRace = race;
407                         break/*ing news: */ iHateJava;
408                     }
409                 }
410                 gameManager.addToGameSummary("Ignoring unknown troll race: " + buf);
411             } while (false);
412         }
413         photoFinish: ; // The race is through, but Java has no goto :-(
414
415         Sprite trollBody = graphicEntityModule.createSprite()
416             .setImage("troll_body.png")
417             .setAnchorX(0.5)
418             .setAnchorY(1)
419             .setTint(trollRace.tint);
420         Sprite trollPantsRed = graphicEntityModule.createSprite()
421             .setImage("pants_red.png")
422             .setAnchorX(0.5)
423             .setAnchorY(1);
424         pantsModule.displayOnPantsState(trollPantsRed, 1);
425         Sprite trollPantsGreen = graphicEntityModule.createSprite()
426             .setImage("pants_green.png")
427             .setAnchorX(0.5)
428             .setAnchorY(1);
429         pantsModule.displayOnPantsState(trollPantsGreen, 2);
430         Sprite trollPantsBlue = graphicEntityModule.createSprite()
431             .setImage("pants_blue.png")
432             .setAnchorX(0.5)
433             .setAnchorY(1);
434         pantsModule.displayOnPantsState(trollPantsBlue, 3);
435         Sprite trollPantsPerv = graphicEntityModule.createSprite()
436             .setImage("pants_perv.png")
437             .setAnchorX(0.5)
438             .setAnchorY(1);
439         pantsModule.displayOnPantsState(trollPantsPerv, 4);
440         troll = graphicEntityModule
441             .createGroup(trollBody, trollPantsRed,
442                          trollPantsGreen, trollPantsBlue, trollPantsPerv)
443             .setX(1920/2)
444             .setY(880)
445             .setScaleX(random.nextInt(2) == 0 ? 1 : -1)
446             .setZIndex(2);
447         trollPositionGauge = graphicEntityModule.createText()
448             .setZIndex(2)
449             .setAnchor(0.5)
450             .setFontSize(40)
451             .setX(1980/2)
452             .setY(980)
453             .setFillColor(0xffffff);
454         moveTroll();
455
456         trollMessage = graphicEntityModule.createText()
457             .setX(1902/2)
458             .setY(680)
459             .setAnchorX(0.5)
460             .setAnchorY(0)
461             .setTextAlign(TextBasedEntity.TextAlign.CENTER)
462             .setStrokeColor(0xFFFF00)
463             .setFillColor(0xFFFF00)
464             .setFontSize(40);
465         pantsModule.displayOnToggleState(trollMessage, "verboseTrolling", true);
466     }
467
468     private void moveTroll() {
469         graphicEntityModule.commitEntityState(0.5, troll, trollPositionGauge);
470         int x0 = p0.castle.getX(), x1 = p1.castle.getX();
471         int y0 = p0.castle.getY(), y1 = p1.castle.getY();
472         troll.setX(x0 + model.trollPosition * (x1-x0) / model.roadLength,
473                    Curve.ELASTIC);
474         troll.setY(y0 + model.trollPosition * (y1-y0) / model.roadLength,
475                    Curve.ELASTIC);
476
477         trollPositionGauge.setX((trollPositionGauge.getX() + troll.getX()) / 2);
478         int distLeft = model.trollPosition;
479         int distRight = model.roadLength - model.trollPosition;
480         if (distLeft <= 0) {
481             trollPositionGauge.setText("← " + distRight);
482         }
483         else if (distRight <= 0) {
484             trollPositionGauge.setText(distLeft + " →");
485         }
486         else {
487             trollPositionGauge.setText(distLeft + " ↔ " + distRight);
488         }
489         graphicEntityModule.commitEntityState(0.75, trollPositionGauge);
490         trollPositionGauge.setX(troll.getX());
491     }
492
493     void moveTroll(Dir d) {
494         moveTroll();
495         gameManager.addToGameSummary(trollRace.starter + " " + d.movement);
496
497         trollMessage.setText(selectTrollMessage(d)).setAlpha(1, Curve.NONE);
498         graphicEntityModule.commitEntityState(0.5, trollMessage);
499         trollMessage.setAlpha(0, Curve.EASE_IN);
500         graphicEntityModule.commitEntityState(1, trollMessage);
501     }
502
503     void animateTurnCounter() {
504         for (int i = 0; i < 10; i++) {
505             turnCounter.setText("T" + _turns + "." + i);
506             // The following line is likely not a bug.
507             graphicEntityModule.commitEntityState((double) i/9, turnCounter);
508         }
509         _turns++;
510     }
511
512     void drawDebug() {
513         String[] debugModePngs = graphicEntityModule.createSpriteSheetSplitter()
514             .setSourceImage("debug.png")
515             .setImageCount(2)
516             .setWidth(900)
517             .setHeight(150)
518             .setOrigRow(0)
519             .setOrigCol(0)
520             .setImagesPerRow(1)
521             .setName("debug")
522             .split();
523         SpriteAnimation debugMode = graphicEntityModule.createSpriteAnimation()
524             .setImages(debugModePngs)
525             .setX(1920 / 2)
526             .setY(60)
527             .setAnchorX(0.5)
528             .setLoop(true);
529         pantsModule.displayOnToggleState(debugMode, "debug", true);
530
531         turnCounter = graphicEntityModule.createText()
532             .setAnchorX(0.5)
533             .setAnchorY(0)
534             .setX(1920 / 2)
535             .setY(260)
536             .setStrokeColor(0xff0080)
537             .setFillColor(0xff0080)
538             .setFontFamily("monospace")
539             .setFontWeight(Text.FontWeight.BOLD)
540             .setFontSize(100);
541         pantsModule.displayOnToggleState(turnCounter, "debug", true);
542         animateTurnCounter();
543     }
544
545     void animateLoss(int x, int y, int size, String message) {
546         int startX;
547         if (x < 1920/2) { startX = 1920; }
548         else if (x > 1920/2) { startX = 1920; }
549         else { startX = 1920 * random.nextInt(2); }
550
551         Text msg = graphicEntityModule.createText(message)
552             .setX(startX)
553             .setY(1080)
554             .setAnchorX(0.5)
555             .setAnchorY(0.5)
556             .setScaleX(3*random.nextDouble() - 1)
557             .setScaleY(3*random.nextDouble() - 1)
558             .setSkewX(2*random.nextDouble() - 1)
559             .setSkewY(2*random.nextDouble() - 1)
560             .setRotation(4*Math.PI * (1 + random.nextDouble())
561                          * (random.nextInt(2) == 0 ? 1 : -1))
562             .setFontSize(0)
563             .setStrokeColor(0xff7f7f)
564             .setFillColor(0xff7f7f)
565             .setFontWeight(Text.FontWeight.BOLD)
566             .setTextAlign(TextBasedEntity.TextAlign.CENTER);
567         graphicEntityModule.commitEntityState(0.25, msg);
568         Curve curve = Curve.ELASTIC;
569         msg.setX(x, Curve.EASE_OUT)
570             .setY(y, Curve.ELASTIC)
571             .setScaleX(1, curve)
572             .setScaleY(1, curve)
573             .setSkewX(0, curve)
574             .setSkewY(0, curve)
575             .setRotation(2*Math.PI * (random.nextDouble() - 0.5), Curve.LINEAR)
576             .setFontSize(size, curve);
577     }
578
579     void doubleDefeat() {
580         gameManager.addToGameSummary(GameManager.formatErrorMessage("Everybody loses!"));
581         animateLoss(1920/2, 680, 150, "L0SERZ!");
582     }
583
584     void draw() {
585         gameManager.addToGameSummary("Draw.");
586         animateLoss(1920/2, 680, 200, "DRAW");
587     }
588
589     String selectTrollMessage(Dir d) {
590         if (random.nextInt(10000) == 0) {
591             return specials[random.nextInt(specials.length)];
592         }
593
594         // yup, still biased
595         int i = random.nextInt(directed.length + isotropic.length / 3);
596         if (i < directed.length) {
597             return directed[i][d.index];
598         }
599         else {
600             return isotropic[random.nextInt(isotropic.length)];
601         }
602     }
603
604     // You'll never remember if you ever saw these…
605     String specials[] = {
606         "Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you",
607         "CG know what they're doing."
608     };
609
610     // Most of what ought to happen in normal play
611     String directed[][] = {
612         // movies
613         { "Han shot first", "I am your father", "Greedo shot first" },
614         { "Inception ends\non level zero", "BRAAAAAAM", "Inception ends\non level one" },
615         { "star wars > star trek", "my god, it's full of troll", "star trek > star wars" },
616         // More movie controversies sought.  Apply on the puzzle contrib page.
617
618         // music
619         { "bach > beethoven", "zimmer is overrated", "beethoven > bach" },
620         { "an octave is 12 semitones", "curse you perfect-pitched ppl", "pianos can play in tune" },
621
622         // everyday life
623         { "tea > coffee", "just drink\nkool-aid", "coffee > tea" },
624         { "Marvel > DC", "Disney > 50 shades", "DC > Marvel" },
625         { "cats > dogs", "humans make\ngood pets", "dogs > cats" },
626         { "the moon landing was staged", "elvis lives", "9/11 was an inside job" },
627         { "santa claus is really\nthe tooth fairy", "the easter bunny tasted yummy", "the tooth fairy is\nreally santa claus" },
628         // Ditto.  Need moar troll.
629
630         // gaming
631         { "PC > console", "pong is still\nunequaled", "console > PC" },
632         { "Windows > Linux", "it's all Android anyway", "Linux > Windows" },
633         { "pad > stick", "mouse gaming is lame", "stick > pad" },
634         { "RTS > FPS", "solitaire best game", "FPS > RTS" },
635         { "YT gaming > twitch", "i watch other ppl play", "twitch > YT gaming" },
636         { "orcs are wusses", "the amulet is in another dungeon", "elves are wusses" },
637         { "here's a link to my patreon", "my apm > yours", "here's my soundcloud" },
638         { "all your stones is belong to us", "all your castle are belong to us", "all your rocks is belong to us" },
639         // I'm not exactly a gamer myself, I take hints on the topics du jour
640
641         // programming
642         { "vi < emacs", "i code with Notepad", "emacs > vi" },
643         { "tabs < spaces", "gofmt FTW", "spaces < tabs" },
644         { "LISP is the most\npowerful language", "HTML is a\nprogramming language", "FORTH is the most\npowerful language" },
645         { "static linking best", "symlinking best", "dynamic linking best" },
646         { "NPE > SIGSEGV", "kernel panic", "SIGSEGV > NPE" },
647         { "objects > functions", "it's closures\nall the way down", "functions > objects" },
648         { "GOTO FTW", "COME FROM FTW", "don't use GOTO" },
649         { "Agile > Waterfall", "SCRUM isn't Agile", "Waterfall > Agile" },
650         // This category's not too bad.
651
652         // Internet
653         { "gmail > github", "copy-paste FTW", "github > gmail" },
654         { "MSIE > Safari", "Opera did it first", "Safari > MSIE" },
655         { "bing > yahoo", "duckduckgo best SE", "yahoo > bing" },
656         { "jira > trello", "bugzilla FTW", "trello > jira" },
657         { "IRC > slack", "chat is work", "discord < IRC" },
658         { "trolls > SJW", "i'm not trolling\njust misunderstood", "SJW > trolls" },
659         { "there's an app for that", "there's a bean for that", "there's an applet for that" },
660         // More always welcome here.
661
662         // CodinGame
663         { "my nn is in python", "my language has -O3", "my code is more than 100k" },
664         { "i found a bug\nin temperatures", "i found a bug on\nthe leaderboard", "i found a bug\nin chuck norris" },
665         { "fix it", "how is ur csb", "ezpz" },
666         { "searcho no chokudai", "GAimax is True AI", "Smitsimax FTW" },
667         { "Automaton2000 > NN", "bots > humans", "AutomatonNN > 2000" },
668         { "optimizing for the contest\ntestcase is cheating", "having moar accounts than cg\nstaff will get you banned", "it's not hardcoding\nif it's stochastic" },
669         { "light theme best", "ascii > graphics", "dark theme best" },
670         { "simulation > heuristics", "true AI is just ifs", "heuristics > simulation" },
671         { "bruteforce FTW", "you'll timeout anyway", "algorithms FTW" }
672         // And here.  Especially as I'm not that active on #World or #Ru.
673     };
674
675     // Those for which I couldn't find a meaningful directednessability.
676     String isotropic[] = {
677         "(unclosed",
678         "Electron apps are the fastest",
679         "Rosebud",
680         "Thanos did nothing wrong",
681         "developers developers developers",
682         "the cloud is just\nother ppl's computers",
683         "ur doin it rong",
684         "tortue",
685         "31OCT = 25DEC",
686         "ASCII stupid question\nget a stupid ANSI",
687         "trolling is a art"
688         // I try and avoid those, but if really it fits nowhere else…
689     };
690 }