-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
1133 lines (919 loc) · 104 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Notes from cory.li</title>
<subtitle>Recent Blog Posts</subtitle>
<id>http://cory.li/feed.xml</id>
<link href="http://cory.li"/>
<link href="http://cory.li/feed.xml" rel="self"/>
<updated>2016-05-11T16:00:00-07:00</updated>
<author>
<name>Cory Li</name>
</author>
<entry>
<title>Keyboard Science with Cherry MLs</title>
<link rel="alternate" href="/cherry-ml/"/>
<id>/cherry-ml/</id>
<published>2016-05-11T16:00:00-07:00</published>
<updated>2016-05-11T16:00:00-07:00</updated>
<author>
<name>Cory Li</name>
</author>
<summary type="html"><p>I was recently inspired by a post / rallying call to “<a href="http://coffeecoder.net/blog/blog-little-things/">blog little things</a>.” So after failing to find detailed information on the Cherry ML pushbutton switch, I’ve decided to publish all my measurement notes in the hopes that it will be helpful to someone...</p></summary>
<content type="html"><p>I was recently inspired by a post / rallying call to &ldquo;<a href="http://coffeecoder.net/blog/blog-little-things/">blog little things</a>.&rdquo; So after failing to find detailed information on the Cherry ML pushbutton switch, I&rsquo;ve decided to publish all my measurement notes in the hopes that it will be helpful to someone else.</p>
<h2 id="the-switch">The Switch</h2>
<p>With mechanical keyboards being all the rage, most enthusiasts are familiar with the <a href="http://cherryamericas.com/product/mx-series/">Cherry MX series</a> of pushbutton switches. Less known, however, is <a href="http://cherryamericas.com/product/ml-series/">Cherry&rsquo;s ML series</a> of low-profile switches, meant for laptops or other space-constrained designs. At 6.9mm total board height, it&rsquo;s less than half the height of a 15.6mm MX switch.</p>
<p><img class="img-polaroid" alt="Cherry MX and ML side-by-side" src="comparison.jpg" /></p>
<ul>
<li><strong>Travel Distance</strong>: 3mm</li>
<li><strong>Force</strong>: 45 cN</li>
<li><strong>Bounce Time</strong>: 5ms</li>
<li><strong>Current Rating</strong>: 10mA</li>
</ul>
<p>The ML switch is technically a &ldquo;family&rdquo; of switches, but there&rsquo;s really only one ML part readily available, the ML1A-11JW. Fortunately, this variant is <a href="https://octopart.com/ml1a-11jw-cherry-916514">widely available in single quantities</a>, making it decent for hobbyist use.</p>
<p>The best description I&rsquo;ve heard for the tactile &ldquo;feel&rdquo; of an ML switch is that it&rsquo;s like a Cherry MX Blue, but &ldquo;scratchier.&rdquo; While preference for a particular switch is mostly dependent on the eye of the beholder, I think these switches feel great in my application: a fully mechanical controller (if you&rsquo;re interested, follow me on <a href="https://twitter.com/cixelyn">twitter</a> for updates!)</p>
<p>Unfortunately, Cherry&rsquo;s <a href="http://cherryamericas.com/wp-content/uploads/2014/12/ml_cat.pdf">official datasheets</a> are frustratingly bare and underannotated, especially in regards to mechanical features, which makes it difficult to use in designs. I will try to elucidate some of the missing information below.</p>
<h2 id="schematic">Schematic</h2>
<p><img src="/cherry-ml/footprint.svg" alt="Cherry ML Footprint" /></p>
<p>The J in the part number stands for <strong>J</strong>umper, meaning that two of the pins are just shorts to each other. However, it isn&rsquo;t clear on exactly which ones they are out of the four. I&rsquo;ve redrawn the footprint with an actual schematic symbol to make it clear that pins 2 and 4 are the actual switch, while 1 and 3 serve as a jumper wire.</p>
<h2 id="mechanical-dimensions">Mechanical Dimensions</h2>
<p><img src="/cherry-ml/drawing.svg" alt="Cherry ML mechanical drawing/dimensions" /></p>
<p>While the MX series has the mounting post on the switch, the ML switch instead has slots that the keycaps insert into. The exact dimensions and spacing of the hole are unclear, so I have filled them out here. These are the dimensions to the best of my knowledge (and the tolerance of my calipers).</p>
<p>The actuator is directly centered over the bottom mounting post, with two 3.1mm x 1.1mm slots separated 5.3mm apart. It is important to note that the large bottom mounting post is <em>not</em> in the center of the switch; it&rsquo;s only 5mm down the edge of the full 11.4mm length. I&rsquo;ve exported a very blocky <a href="/cherry-ml/ML1A-11JWv1.step">3D STEP file</a> out for visualization use while working in your favorite EDA package.</p>
<h2 id="keycap-test">Keycap Test</h2>
<p>To check my measurements, I designed a small button cap with 3 x 1mm mounting posts as a test.</p>
<p><img src="/cherry-ml/keycap.svg" alt="Cherry ML round button keycap design" /></p>
<p>The button yielded a reasonable fit after printing it out on an SLA 3D-printer. </p>
<p><img class="img-polaroid" alt="Cherry ML with 3D printed keycap" src="keycap-off.jpg" /></p>
<p>It may be possible to increase the size of the mounting post a bit to make the fit tighter, but so far I&rsquo;m pretty happy with how it turned out.</p>
<p><img class="img-polaroid" alt="Cherry ML with 3D printed keycap" src="keycap-on.jpg" /></p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>I&rsquo;ll try to make this a &ldquo;living&rdquo; blog post in that I&rsquo;ll periodically update it with more accurate measurements and models as I make them &ndash; feel free to check back for the latest information. At some point I plan to take the keycap apart and optically scan and measure each piece, but for now the above is sufficient for my purposes.</p>
<p>Also, if there&rsquo;s anyone out there with better measurements or models, please feel free to reach out to me either through <a href="https://twitter.com/cixelyn">twitter</a> or <a href="mailto:[email protected]">email</a> so I can correct my drawings!</p>
<h2 id="changelog">Changelog</h2>
<ul>
<li><strong>5/11/2016</strong>: Initial Posting</li>
</ul>
</content>
</entry>
<entry>
<title>Fun with solving puzzles (and dragons)</title>
<link rel="alternate" href="/puzzle-and-dragons/"/>
<id>/puzzle-and-dragons/</id>
<published>2016-03-26T15:00:00-07:00</published>
<updated>2016-03-26T15:00:00-07:00</updated>
<author>
<name>Cory Li</name>
</author>
<summary type="html"><p>From the end of 2012 to early 2014, I found myself enchanted (read: entrapped) by the mobile game known as Puzzles and Dragons. A deceptively simple and charming game, this post is a collection of my musings on the gameplay and design. It’s also some...</p></summary>
<content type="html"><p>From the end of 2012 to early 2014, I found myself enchanted (read: entrapped) by the mobile game known as Puzzles and Dragons. A deceptively simple and charming game, this post is a collection of my musings on the gameplay and design. It&rsquo;s also some closure for myself so that I can finally say that I have &ldquo;beaten&rdquo; the game and put it to rest.</p>
<p>Anyways, there are really four separate posts contained within this mind dump, feel free to skip to the parts of interest to you:</p>
<ol>
<li><a href="#core-gameplay">Introduction to PAD and gameplay overview</a></li>
<li><a href="#solvable-gameplay">Some PAD maths and algorithms</a></li>
<li><a href="#complete-computer-control">PAD hacking/automation</a></li>
<li><a href="#design-thoughts">Thoughts on PAD&rsquo;s game design</a></li>
</ol>
<h2 id="core-gameplay">Core Gameplay</h2>
<p>Puzzles and Dragon (or PAD for short) is a match-three puzzler from the Japanese studio GungHo entertainment. Featuring Pokemon-like collection and progression elements, it ranks among one of the most profitable apps in the world. It still pulls in around <a href="http://venturebeat.com/2013/05/13/puzzle-dragons-is-making-3-75m-a-day/">3 million dollars <em>daily</em></a> and was the first mobile app ever to hit <a href="http://www.pocketgamer.biz/asia/news/57076/its-official-puzzle-and-dragons-is-the-first-mobile-game-to-1-billion-in-revenue/">$1 billion dollars in revenue</a>.<sup id="fnref1"><a href="#fn1">1</a></sup></p>
<p>The goal of the game is very simple: Eliminate 3 or more orbs in a row.</p>
<video autoplay loop muted style="max-width:350px;" >
<source src="overview.mp4" type="video/mp4">
<source src="overview.webm" type="video/webm">
Your browser doesn&rsquo;t support HTML5 video tag.
</video>
<p>Matched combos on the bottom half of the game board build attack power which you use to launch attacks against cute enemy monsters shown on the top half.</p>
<p>But notice that there is a subtle difference in game design which sets PAD apart from other match-threes, like Candy Crush or Bejeweled. Instead of your typical swap-two-elements, a single piece in PAD can be moved an arbitrary length, displacing other pieces as it travels.</p>
<p>The ingenuity in this design is that it&rsquo;s actually a strict <em>superset</em> of Bejeweled&rsquo;s gameplay. It makes the game incredibly beginner-friendly, since you can still play in a very simplistic swap-two manner:</p>
<video autoplay loop muted style="max-width:400px" >
<source src="simple.mp4" type="video/mp4">
<source src="simple.webm" type="video/webm">
Your browser doesn&rsquo;t support HTML5 video tag.
</video>
<p>But the more you play, the more it becomes clear that the skill ceiling is actually incredibly high, as the player learns to massage the board into their desired configuration with lengthy combos:</p>
<video autoplay loop muted style="max-width:400px" >
<source src="combo.mp4" type="video/mp4">
<source src="combo.webm" type="video/webm">
Your browser doesn&rsquo;t support HTML5 video tag.
</video>
<p>With this simple core mechanic, PAD is able to create one of the best gameplay skill-progression tracks I have seen in any mobile game. Without the need for preprogrammed experience bars or player buff handouts, there is still an invisible but very prominent feeling of &ldquo;leveling-up&rdquo;. The player gains new advanced techniques, combo setups, and become more dexterous at manipulating the orbs all on their own.</p>
<p>And most impressive of all, this invisible progress track guides the player all the way from being a casual bejeweled player to tackling PAD&rsquo;s version of ruthless World of Warcraft-like endgame raiding.</p>
<p>As an example, study the following hypothetical 4x3 board:</p>
<p><img class="img-polaroid" title="Ta-da!" alt="hypothetic four-color 4x3 board" width="400px" src="cutout.png" /></p>
<p>A beginner might go for one of the two easy double-combos:</p>
<p><img class="img-polaroid" title="And similarly for the left side" alt="Path for a single combo on the right side" width="400px" src="cutout-path1.png" /></p>
<p>A more experienced player should immediately see the path for the full four vertical combo:</p>
<p><img class="img-polaroid" title="Magic!" alt="Path for four vertical combos" width="400px" src="cutout-path2.png" /></p>
<p><a href="http://pad.dawnglare.com/?s=iahWBP0">Try it for yourself</a> if you&rsquo;re having trouble understanding the path. And of course these pattern identifications are much more impressive when in context of the full 5x6 board.</p>
<p><img class="img-polaroid" title="Do the above moves and you still have an easy purple and blue setup too. Hearts for the extra ambitious." alt="Path for double combo" width="400px" src="cutout-full.png" /></p>
<p>This incredibly high skill ceiling actually makes PAD quite entertaining to watch, as skill is so visibly demonstrable. If you don&rsquo;t believe me, check out one of my favorite <a href="https://www.youtube.com/watch?v=ESkW2D_1nrk">PAD videos</a>, or even watch some of the official <a href="https://youtu.be/347dVAcjA9A?t=2160">AppBank streams</a>. A spectator may mentally plan out her own solution, only to be enlightened when a master player steps up to move the orbs. To the untrained eye, it look as if orbs are being magically expelled from the board under a ruthless finger with machine-like precision<sup id="fnref2"><a href="#fn2">2</a></sup>.</p>
<h2 id="solvable-gameplay">Solvable Gameplay</h2>
<p>So after one dropped orb too many, it occured to me that I should just program the computer to play PAD for me.</p>
<p>The way orb manipulation works makes it very similar to the classic <a href="https://en.wikipedia.org/wiki/15_puzzle">15-puzzle</a>, in which you slide around numbered tiles in a grid to rearrange them into numerical order.</p>
<p><img src="/puzzle-and-dragons/15-puzzle.svg" title="These things were designed to shut me up as a little kid during long road trips" alt="15 Puzzle" /></p>
<p>Roughly speaking, PAD is an MxN generalization of 15-Puzzle. The only difference is that in PAD there is no explicit &ldquo;hole,&rdquo; the hole is instead the tile that you are currently dragging under your finger.</p>
<p>Using this as a bit of scaffolding, we can break the plan of attack for &ldquo;solving&rdquo; PAD into 2 parts: calculate the board with the maximal score, and then calculate the shortest path to get from our current state to our desired state.</p>
<p>An easy way to produce the maximal scoring board is to sort all the orbs by color, and then pack groups of three starting from the bottom<sup id="fnref3"><a href="#fn3">3</a></sup>.</p>
<p><img class="img-polaroid" title="Fortunately for us, this isn't the full bin packing problem" alt="Packing groups of three starting from the bottom" width="400px" src="packing.png" /></p>
<p>With some handwaving, you can show you can do no better than this configuration (i.e. breaking up a group to produce a falling combo does not increase your total combo count, so there is no advantage to not packing tight adjacent groups of threes). Note of course that this doesn&rsquo;t take into account the mechanic of <em>skyfalls</em>, that is, the additional combos scored serendiptiously from orbs refilling the board. To maximize this, you&rsquo;ll want to simultaniously pack in the largest number of <em>cascades</em>. </p>
<p><img class="img-polaroid" title="Unfortunately for us, packing in this style is a bit harder. This example has a total of 4 cascades." alt="Packing cascades starting from the bottom" width="400px" src="cascades.png" /></p>
<p>Because the game refills orbs from the top each time it clears away a matched combo, having this <em>cascade</em> of combos means that the falling orbs are permuted several times on the way down for a statistically higher chance of matches.</p>
<p>A tricky question is whether we can actually get to this desired configuration. Going back to our scaffolding, consider the fact that in 15-puzzle not every board state is actually reachable! There&rsquo;s a neat little theorem showing that any move in 15-puzzle preserves the parity of inversions<sup id="fnref4"><a href="#fn4">4</a></sup> &ndash; that is, the number of times a higher-numbered tile precedes a lower-numbered tile. This fact partitions the space of possible board states into two disconnected graphs: those of even parity and those of odd parity. From any board, you can reach every other board of the same parity, but never one of the other parity! This is why if you physically pull out and swap two consecutive pieces on a 15-puzzle board, the puzzle is no longer solvable.</p>
<p>Unlike 15-puzzle where there are 15 unique pieces however, there are only 7 unique orb colors in PAD (if you also count poison as a color). Every board therefore must have a duplicate orb somewhere, and the existence of that duplicate means you can always swap the two duplicates to &ldquo;change&rdquo; the parity without actually changing the state of the board. Therefore, we can show that in PAD, <em>it is possible to achieve any desired board state</em> &ndash; the only limitation is your skill (and time to manipulate the orbs).</p>
<p>So, given that we know the reachable maximal-scoring board, we just need to write a solver to get there!</p>
<p>Turns out this is somewhat challenging, as finding the shortest solution for 15-puzzle is NP-hard. Likewise, while figuring out the highest scoring board in PAD is reasonably easy, finding the shortest path to achieve the highest-scoring board in the alloted time is non-trivial. </p>
<p>Fortunately for us, we can really only do so many moves in the alloted four seconds, so a non-exhaustive depth-first search is &ldquo;good enough&rdquo; for all intents and purposes. <a href="http://kennytm.github.io/pndopt/pndopt.html">Pndopt</a> is one such app<sup id="fnref5"><a href="#fn5">5</a></sup>, which lets you weight certain colors for any given situation. Like a lot of F2P games these days, the game time-gates you on the number of plays you can do in a day, so for players who are running hard dungeons, it is not unusual to input every move through pndopt to maximize chances of success &ndash; something of which I am quite guilty.</p>
<p>To PAD&rsquo;s credit, using a computer to solve the puzzles surprisingly doesn&rsquo;t ruin everything &ndash; it just removes the puzzler cornerstone and transforms the game into more of a RPG team management simulation. </p>
<h2 id="complete-computer-control">Complete Computer Control</h2>
<p>Given that most people are using computer solvers, why not just have the computer play the game entirely? Back when I was still <del>addicted</del> playing, I hacked up a <a href="https://github.com/Cixelyn/pndsolver">proof of concept solver &amp; runner</a> and threw it up on github just for myself. </p>
<p><img class="img-polaroid" title="This is where I developed my love/hate relationship with angular.js" alt="pndsolver web interface" src="pndsolver.png" /></p>
<p>Here&rsquo;s how it all works:</p>
<ol>
<li> Screen capture is accomplished with <code>idevicescreenshot</code> on iOS and <code>adb screencap</code> on android.</li>
<li>Once the image is on the computer, the location of the 6x5 grid is calculated from the screenshot aspect ratio and then divided into 30 individual images.</li>
<li>The average hue of the individual image determines the color.</li>
<li>SIFT is run against a grayscale version of the image to give a list of key points, which is then matched against a list of possible orb modifiers (e.g. the plus modifiers, which give a 1.05x bonus to matches)</li>
<li>Candidate combo paths are obtained via an extremely lazy DFS written in python, which runs a &ldquo;multicore&rdquo; solver by spawning a bunch of <code>pypy</code> instances for different regions of the board.</li>
<li>Solutions with a score above a certain threshold are presented to the user along with the required path. The user can then sort through the solutions by relevant parameters such as damage done or health healed.</li>
<li>On android only, the chosen path can then be executed on the device via android&rsquo;s <code>monkeyrunner</code> tool. (I wasn&rsquo;t able to figure out a way to programmatically simulate touches on iOS).</li>
</ol>
<h3 id="straight-up-cheating">Straight Up Cheating</h3>
<p>Of course, this is an absurd amount of work just to play a game that is entirely client-side. Turns out PAD mothership doesn&rsquo;t even care about the state of the game. Sniffing the traffic shows that there are only a total of three requests made per dungeon:</p>
<ol>
<li><code>sneak_dungeon</code> - Client makes this request in order to enter a dungeon. Server responds with dungeon encounters and loot table in response.</li>
<li><code>sneak_dungeon_ack</code> - Client responds that the dungeon layout has been received and that the player is now playing. This is done in case of connectivity issues.</li>
<li><code>clear_dungeon</code> - Client responds that the dungeon is cleared. Server acknowledges, confirms the received loot, and updates the player&rsquo;s account.</li>
</ol>
<p>Note that what most people could call the &ldquo;core game&rdquo; is actually <em>entirely clientside</em>. This includes the board state, monster attacks, monster damage, player health, etc. The entirety of the player&rsquo;s efforts is boiled down to either a single http request &ndash; a success request nets them the entire loot table, while a failure request leaves them with nothing.</p>
<p>One nice/convenient aspect of this design is that you can actually &ldquo;queue&rdquo; dungeons before losing connectivity. I&rsquo;d often load a dungeon before entering the subway, play through it during my 10 minute commute downtown, then re-sync once I surfaced at the destination station.</p>
<p>Some other fun notes from packet sniffing:</p>
<ul>
<li>PAD to me is the best testament of the &ldquo;just ship it&rdquo; mentality as it appears the whole thing was written in PHP (i.e. the request is made to <code>sneak_dungeon.php</code>), showing that a fancy stack isn&rsquo;t necessary to build a billion dollar game.</li>
<li>The API endpoint to enter the dungeon <code>sneak_dungeon</code> is probably an amusing mistranslation of sorts - probably originally along the lines of &ldquo;to enter the dungeon discretely / carefully&rdquo;</li>
<li>Somewhere around the 5.X series patches, they started encrypting the JSON payload so that it wasn&rsquo;t easily over-the-wire sniff-able. Clever players were checking the loot-table ahead of time to determine whether a dungeon was even worth running. It&rsquo;s now sent as a encoded binary base64 blob &ndash; seems like a fun and reasonably straight-forward reverse engineering project for someone&rsquo;s weekend.</li>
<li>Monsters are actually referred to as <code>cards</code> in all the API calls. Maybe early prototypes of the games were meant to feel more like a collectible-card game?</li>
</ul>
<h2 id="design-thoughts">Design Thoughts</h2>
<p>If you&rsquo;re willing to ignore how easy it is to cheat and just play the game as it&rsquo;s meant to be played, it&rsquo;s actually quite an enjoyable experience. There are a lot of minor annoyances in PAD, but I think that they designed two high-level mechanics down quite well:</p>
<h3 id="resource-management">Resource Management</h3>
<p>I&rsquo;m not sure if the monster fusion mechanic was invented by PAD, but I find it to be a very clever bit of design. The basic gist is this: monsters are the primary form of &ldquo;currency&rdquo; in PAD.</p>
<ul>
<li>You use teams of monsters to clear dungeons</li>
<li>Clearing dungeons sometimes rewards you with additional monsters</li>
<li>Excess monsters can be used as a source of experience points for other monsters by &ldquo;feeding&rdquo; them</li>
</ul>
<p>Often, you&rsquo;ll want to save the strongest monsters or put them on your team while feeding the weaker ones away. When feeding, feeding five fodder monsters at a time is slightly more efficient than feeding one at a time. So overall, the player is encouraged to hoard monsters.</p>
<p>Even getting duplicate monsters is exciting as fusing duplicates together not only provides experience, but also levels up the monster&rsquo;s powerful &ldquo;active skill&rdquo;.<sup id="fnref6"><a href="#fn6">6</a></sup></p>
<p>Countering the natural hoarding tendency is the concept of &ldquo;Box Space,&rdquo; or the total number of monsters you&rsquo;re allowed to hoard at a time. Exceeding the allocated box space prevents the player from being able to enter new dungeons, forcing them to make decisions about consolidating powerful creatures together, <em>or</em> spending IAP purchases on box space expansions.</p>
<p>I find this single monster resource system to be quite elegant<sup id="fnref7"><a href="#fn7">7</a></sup>, as it both simplifies the number of resources in the game, but also provides interesting decisions that players can think about in the downtime between dungeons: given a limited amount of box space, which monsters should I keep, and which ones should I feed away for experience?</p>
<h3 id="raiding">Raiding</h3>
<p>Another mechanic I really like a lot is PAD&rsquo;s treatment of the end-game.</p>
<p>The designers were either really clever, or got really lucky, in their design of the limited-time event system. In the game, there is a list of unique &ldquo;special dungeons&rdquo; called &ldquo;descends&rdquo; which rotates every 24 hours. Each special dungeon guarantees the drop of a unique monster only if you are able to clear it on the hardest difficulty. <em>However</em>, specific special dungeons only come around about once a month. So if you want a particular monster, you have to train your team and plan to be ready by the dungeon date.</p>
<p>The whole preparation and timing feels very much like &ldquo;gearing up&rdquo; for a raid, as is common in other MMOs. Players will often only have two to three shots at the dungeon due to the time-gating, so they will often spend the days leading up to the descend training their monsters, reading up on the boss mechanics, and browsing the community to find friends with monsters who can help tackle the level<sup id="fnref8"><a href="#fn8">8</a></sup>.</p>
<p>Due to the power spike granted by the newly acquired monsters from beating the descend, there&rsquo;s also a natural progress of descends, just as there is often a natural raiding progression in MMOs. Often, the first real descend new players tackle is <a href="http://puzzledragonx.com/en/mission.asp?m=810">Hera</a>,<sup id="fnref9"><a href="#fn9">9</a></sup> which provides an ability called Gravity, dealing an unconditional 30% damage to enemy monsters. Using Hera, they work their way through harder and harder stages, like Valkyrie, Goemon, eventually building a team that can tackle Zeus, Satan, and the other end-game descends.</p>
<p>This feeling of end-game progression complements the skill progression well, making me unsurprised that the game is still doing well after four years.</p>
<h2 id="closing">Closing</h2>
<p>Anyways with over 500 days logged into the game, I think it&rsquo;s time to put this to rest. Here&rsquo;s a screenshot of my core team. </p>
<p><a href="team.png"><img title="800 box space later..." alt="Cory's PAD Team" src="team_small.png" style="max-width:320px" /></a>
<a href="karin.png"><img title="I'll miss you Karin!" alt="Karin Seiryuu" src="karin_small.png" style="max-width:320px" /></a></p>
<p>Farewell Karin!</p>
<p><img alt="Uninstalling..." src="uninstall.png" style="max-width:100px" /></p>
<p><br></p>
<p><em>I recently started using twitter more, feel free to follow me <a href="http://twitter.com/cixelyn">@cixelyn</a> if you enjoy my writing. Also, special thanks to Ruwen Liu, Haitao Mao, Sam Powers, and YP Chen for reading drafts of this post.</em></p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p>And as a self-congratulatory note, I am proud to say that I managed to spend less than $100 on IAP, making this one of the best time/money sources of entertainment I have ever played. Pyrrhic victory I suppose.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>
<li id="fn2">
<p>To be completely fair, there is a part of PAD that can be quite inscrutable to uninitiated viewers: intentional board stalling. The idea is that the player does not actually want to trigger a big combo because it would prematurely advance them to the next part of the level before all their special abilities are charged. So they make a calculated (and often short) move that makes only a single match, while still manipulating the overall board layout to trigger a big combo later.&nbsp;<a href="#fnref2">&#8617;</a></p>
</li>
<li id="fn3">
<p>This is of course for the very basic case where you have a rainbow colored team which each member of equal power. The analysis is much more nuanced if you care about a non-uniform team (i.e. you&rsquo;re stuck with an integer linear programming problem). For those that care, the basic damage formula is <span>$$ (1+\frac{combos-1}{4}) \cdot \sum_{n=0}^{combos} \mathrm{attack}(n)\cdot(1 +\frac{orbs(n)-3}{4}) $$</span>where combos is the total number of combos, attack(n) is the total monster attack power of combo<sub>n</sub>, and orbs(n) is the total number of orbs in combo<sub>n</sub>. Throw in board modifiers, monster multipliers, and a whole host of other powerups, and the calculation becomes really messy.&nbsp;<a href="#fnref3">&#8617;</a></p>
</li>
<li id="fn4">
<p>For the general MxN puzzle, any transposition will preserve the invariant N mod 2, where N is the number of inversions plus the row number of the empty square. For a more thorough treatment, see the excellent resource at <a href="http://www.cut-the-knot.org/pythagoras/fifteen.shtml">Interactive Mathematics Miscellany and Puzzles</a>.&nbsp;<a href="#fnref4">&#8617;</a></p>
</li>
<li id="fn5">
<p>If you do use Pndopt, I find their default <code>MAX_SOLUTIONS_COUNT</code> a bit too low. Open the console and bump the variable to something reasonable like 20,000.&nbsp;<a href="#fnref5">&#8617;</a></p>
</li>
<li id="fn6">
<p>Each monster may have up to one <em>active skill</em>, which is a player-activated ability that provides some sort of positive benefit during battles. &ldquo;Orb Changers&rdquo; are the most sought-after active skill as they typically convert all orbs of one color to another, serving as play-makers for difficult board situations. Leveling up a skill reduces the skill&rsquo;s cooldown timer, allowing the player to use it more frequently in battle.&nbsp;<a href="#fnref6">&#8617;</a></p>
</li>
<li id="fn7">
<p>To be completely fair, PAD actually has a secondary resource called &ldquo;gold&rdquo; which I find quite inelegant. Except very early on in the game, you never run out of gold, making it a non-resource. I think the designers realized this mistake and started adding gold sinks in the form of purchasable dungeons around the 6.0 patch.&nbsp;<a href="#fnref7">&#8617;</a></p>
</li>
<li id="fn8">
<p>When battling a dungeon, you provide one leader and four team members. You also have the option of using a friend&rsquo;s monster, who serves as the team&rsquo;s second leader. Leader monsters give huge team buffs, so having a roster of strong friends is paramount to fielding an overall strong team. Many higher-level players will often lend their monsters to beginners during the big descend days. Common places to look for specific friends include <a href="http://reddit.com/r/puzzleanddragons">/r/puzzleanddragons</a> and puzzledragonx&rsquo;s <a href="http://puzzledragonx.com/en/friendfinder.asp">friend finder</a>.&nbsp;<a href="#fnref8">&#8617;</a></p>
</li>
<li id="fn9">
<p>I know this isn&rsquo;t entirely the case as Hera was bumped to a normal dungeon now, but it was true throughout over half of PAD&rsquo;s life and the entirety of my PAD career.&nbsp;<a href="#fnref9">&#8617;</a></p>
</li>
</ol>
</div>
</content>
</entry>
<entry>
<title>Java bytecode hacking for fun and profit</title>
<link rel="alternate" href="/bytecode-hacking/"/>
<id>/bytecode-hacking/</id>
<published>2014-01-06T12:59:00-08:00</published>
<updated>2014-01-06T12:59:00-08:00</updated>
<author>
<name>Cory Li</name>
</author>
<summary type="html"><p>With the <a href="http://battlecode.org">2014 season of battlecode</a> starting tomorrow, I figured now would be a good as time as any to finally write up my notes on bytecode hacking. If you’re unfamiliar with Battlecode, a <a href="http://cory.li/battlecode-intro/">good introduction is my previous post</a> (tldr: it’s an intense...</p></summary>
<content type="html"><p>With the <a href="http://battlecode.org">2014 season of battlecode</a> starting tomorrow, I figured now would be a good as time as any to finally write up my notes on bytecode hacking. If you&rsquo;re unfamiliar with Battlecode, a <a href="http://cory.li/battlecode-intro/">good introduction is my previous post</a> (tldr: it&rsquo;s an intense open-to-all programming competition where teams write AIs for virtual robot armies).</p>
<p>You might be wondering what bytecodes have to do with battlecode. Well, one of the most intriguing parts of the battlecode engine is the cost model applied to each team&rsquo;s AI. In order to hard limit each team&rsquo;s total computation, yet guarantee equal computation resources to each team, each team is given a bytecode limit, and their code is instrumented and allowed to run only up to that limit before it is halted. This is pretty counter-intuitive for people who are used to more traditional time-based computational limits.</p>
<p>For those unfamiliar with bytecodes, they are the atomic instructions that run on the JVM &ndash; your Java source compiles down to them, similar to assembly. The tricky part is that Battlecode keeps this bytecode limit low &ndash; typically in the 6-10k range. To give a rough sense of scale, an A* search through a small 8x8 grid can easily blow through the whole computational budget; Battlecode maps, however, can be anywhere from 20x20 to 60x60 tiles in size.</p>
<p>This bytecode limit, then, is actually quite interesting, as it forces teams to come up with novel and creative ways to solve problems rather than just implementing well-known algorithms. Unfortunately, it also serves as one of the major contributors to a relatively steep learning curve. My goal with this post is to elucidate just what is happening under the hood, as well as provide some tips and tricks for teams to squeeze every last drop of performance out their AIs.</p>
<p>As a disclaimer, these optimizations should be performed last, once the majority of your AI framework is built; writing good code is better than optimizing incorrect or algorithmically poor code. But that being said, when you&rsquo;re tight for bytecodes, any small optimization can very well mean the difference between victory and defeat.</p>
<hr>
<p>When searching for resources on the net, it becomes apparent that bytecode optimization is something of a lost art &ndash; any article on the topic comes from pre-2000, before the HotSpot JIT compiler was introduced in Java 1.3. With JIT compilation and also modern obfuscation engines like proguard, there hasn&rsquo;t been much reason to pay attention to things like emitted bytecode or total class file size. Battlecode is somewhat unique as contestants are required to turn in their source, rather than compiled code (as students can take it as a course and count it for university credits). Thus, we must turn to these old techniques to to control emitted bytecodes from high-level source.</p>
<p>There are some who may scoff at bytecode optimization, reasoning that it&rsquo;s a worthless skill for modern computer science, especially those working in high-level languages. Understanding what the compiler emits however is a skill still very much alive and well in embedded programming, FPGA programming, and other performance-oriented disciplines. In FPGA programming, one must have a mental model of what hardware will be synthesized <em>before</em> writing the code. In embedded programming, the frequency of software-based signal generation is limited by instruction count in the loop body.</p>
<p>Honestly, most of the reward of bytecode optimization comes from being able to play with the battle-tested JVM architecture in a particularly novel way. It&rsquo;s incredibly fun, especially when it gives you the edge against rival teams.What more justification does an interested hacker need?</p>
<h3 id="jvm-bytecode-basics">JVM Bytecode Basics</h3>
<p>To understand how to work around a bytecode limit, we must first understand the JVM&rsquo;s execution model. The inner workings of the JVM are <a href="http://blog.jamesdbloom.com/JVMInternals.html">well documented elsewhere on the net</a> &ndash; feel free to skip this section if you&rsquo;re already familiar, but for those who aren&rsquo;t, here&rsquo;s a brief overview of the important parts.</p>
<p>The JVM is a relatively simple stack-based architecture with a fairly comprehensive instruction set allowing for manipulation of both primitives and full objects. An atomic instruction is called a bytecode, roughly equivalent to a single assembly instruction in native code. These bytecodes are stored as a stream within the compiled Java .class file, and are executed within the context of a stack frame.</p>
<p>Each stack frame contains:</p>
<ol>
<li>The current operand stack</li>
<li>An array of local variables</li>
<li>A reference to the constants pool of the class of the current method</li>
</ol>
<p><img src="/bytecode-hacking/jvm-diagram.png" alt="JVM Stack Frame Overview" /></p>
<p>Bytecodes perform computation by pushing and popping values onto the current frame&rsquo;s operand stack. If a method is invoked, a brand new frame is created and pushed on top of the execution stack. Upon method completion, the frame is destroyed and the return value is passed to the previous frame.</p>
<p>The easiest way to understand bytecode execution is to see an example. Given the following Java code:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kt">int</span> <span class="nf">sumSquares</span><span class="o">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">,</span> <span class="kt">int</span> <span class="n">b</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">rv</span> <span class="o">=</span> <span class="n">a</span><span class="o">*</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="o">*</span><span class="n">b</span><span class="o">;</span>
<span class="k">return</span> <span class="n">rv</span><span class="o">;</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>Lets disassemble it and see how it works. The standard Java SDK conveniently comes with the <code>javap</code> disassembler. <code>javap -c Main</code> will give you the bytecode stream for <code>Main.class</code> in the same directory, which I&rsquo;ve (overly)annotated<sup id="fnref1"><a href="#fn1">1</a></sup> to explain how it works:</p>
<div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre>public int sumSquares(int, int);
Code:
0: iload_1 // push local variable 1 (int a) onto the stack
1: iload_1 // push int a onto the stack again
2: imul // pop two ints, multiply them, then push the result onto the stack
3: iload_2 // push local variable 2 (int b) onto the stack
4: iload_2 // push int b onto the stack again
5: imul // pop two ints, multiply, and push back the result
6: iadd // pop the two results, add them, and push back the result
7: istore_3 // store the result to local variable 3 (rv)
8: iload_3 // load local variable 3
9: ireturn // return what's on the stack (rv)
</pre></td></tr></tbody></table></code></pre></div>
<p>Note how the assignment of local variables to array positions is determined at compile time and baked directly into the byte code stream. The two parameters are passed in as positions 1 and 2 on the locals array while <code>rv</code> has been assigned position 3 on the array. In fact, the bytecode output from the Java source was fairly predictable &ndash; we&rsquo;ll use this fact to our advantage later on.</p>
<p>Here&rsquo;s another simple example that contains branching:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kt">int</span> <span class="nf">sign</span><span class="o">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span><span class="o">(</span><span class="n">a</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">a</span><span class="o">&gt;</span><span class="mi">0</span><span class="o">)</span> <span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">else</span> <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div><div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre>public int sign(int);
Code:
0: iload_1
1: ifge 6
4: iconst_m1
5: ireturn
6: iload_1
7: ifle 12
10: iconst_1
11: ireturn
12: iconst_0
13: ireturn
</pre></td></tr></tbody></table></code></pre></div>
<p>As you may have noticed, the numbers on the left are not actually instruction count, but rather the instruction&rsquo;s byte-offset from the beginning of the stream. The jump targets for <code>ifge</code> and <code>ifle</code> are specified in terms of these offsets.</p>
<p>With these basics in mind, we can now take a look at how to optimize algorithms from within the Battlecode engine.</p>
<h3 id="bytecode-counting">Bytecode Counting</h3>
<p>The current generation of <a href="https://github.com/battlecode/battlecode-2013-server/blob/master/src/main/battlecode/engine/instrumenter/RoboMethodTree.java">Battlecode&rsquo;s instrumentation engine</a> uses the <a href="http://asm.ow2.org/">OW2 ASM framework</a> for bytecode counting. Before a team&rsquo;s code is executed, the engine walks through the generated program tree, and computes the bytecode cost of each <a href="http://en.wikipedia.org/wiki/Basic_block">basic block</a>. At each block&rsquo;s exit, a checkpoint is injected with the block&rsquo;s total cost. During live execution, these checkpoints increment the AI&rsquo;s internal total bytecode counter. If at any checkpoint the running tally exceeds <code>GameConstants.BYTECODE_LIMIT</code>, the AI&rsquo;s execution is halted and execution of the next robot&rsquo;s AI begins. This essentially means that the executing robot&rsquo;s turn is skipped &ndash; preventing it from moving or firing its weapons if it hadn&rsquo;t already done so that turn, which can be devastating in combat.</p>
<p>The system&rsquo;s design allows the engine to simulate hundreds of AIs efficiently, with only moderate overhead. Earlier versions of battlecode ran on a custom JVM implementation written in Java, and while it could instrument on a per-instruction-basis, was a lot slower.</p>
<p><strong>The biggest takeaway is that you are penalized <em>only</em> for the total number of bytecodes you use.</strong></p>
<p>A common mistake when looking at disassembled code is that the <em>size</em> of the bytecode does not matter: <code>iload_0</code> (0x1a) which is a one byte special compact instruction for loading the integer from local variable 0, is the same cost as the the two byte <code>iload #5</code> (the <code>iload</code> opcode 0x15, followed by the argument 0x05):</p>
<div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>0: iload_0 0: iload #5
1: return 2: return
</pre></td></tr></tbody></table></code></pre></div>
<p>When checking output from <code>javap</code> or other disassemblers, you must remember to renumber from byte-offset to instruction-offset in order to know your total cost.</p>
<p>The <em>complexity</em> of the bytecode instruction doesn&rsquo;t matter either. As an example, here are two equivalent statements that emit two different bytecodes:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">if</span><span class="o">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span> <span class="n">methodA</span><span class="o">();</span>
<span class="k">else</span> <span class="nf">if</span><span class="o">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">2</span><span class="o">)</span> <span class="n">methodB</span><span class="o">();</span>
<span class="k">else</span> <span class="nf">if</span><span class="o">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">3</span><span class="o">)</span> <span class="n">methodC</span><span class="o">();</span>
<span class="k">else</span> <span class="nf">if</span><span class="o">(</span><span class="n">choice</span> <span class="o">==</span> <span class="mi">4</span><span class="o">)</span> <span class="n">methodD</span><span class="o">();</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>while compiles to<sup id="fnref2"><a href="#fn2">2</a></sup>:</p>
<div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre> 0: aload_0
1: getfield #45 // Field choice:I
4: iconst_1
5: if_icmpne 15
8: aload_0
9: invokevirtual #47 // Method methodA:()V
12: goto 57
15: aload_0
16: getfield #45 // Field choice:I
19: iconst_2
20: if_icmpne 30
23: aload_0
24: invokevirtual #49 // Method methodB:()V
27: goto 57
30: aload_0
31: getfield #45 // Field choice:I
34: iconst_3
35: if_icmpne 45
38: aload_0
39: invokevirtual #51 // Method methodC:()V
42: goto 57
45: aload_0
46: getfield #45 // Field choice:I
49: iconst_4
50: if_icmpne 57
53: aload_0
54: invokevirtual #53 // Method methodD:()V
</pre></td></tr></tbody></table></code></pre></div>
<p>and the same expression written as a switch statement:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="k">switch</span><span class="o">(</span><span class="n">choice</span><span class="o">)</span> <span class="o">{</span>
<span class="k">case</span> <span class="mi">1</span><span class="o">:</span>
<span class="n">methodA</span><span class="o">();</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="mi">2</span><span class="o">:</span>
<span class="n">methodB</span><span class="o">();</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="mi">3</span><span class="o">:</span>
<span class="n">methodC</span><span class="o">();</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="mi">4</span><span class="o">:</span>
<span class="n">methodD</span><span class="o">();</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div><div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre> 0: aload_0
1: getfield #45 // Field choice:I
4: tableswitch { // 2 to 6
2: 40
3: 47
4: 54
5: 65
6: 61
default: 65
}
40: aload_0
41: invokevirtual #47 // Method methodA:()V
44: goto 65
47: aload_0
48: invokevirtual #49 // Method methodB:()V
51: goto 65
54: aload_0
55: invokevirtual #51 // Method methodC:()V
58: goto 65
61: aload_0
62: invokevirtual #53 // Method methodD:()V
</pre></td></tr></tbody></table></code></pre></div>
<p>Notice how the if-else statements emit sequential <code>if_icmpne</code> instructions which much be evaluated in order, while the switch statement emits a single <code>lookupswitch</code><sup id="fnref3"><a href="#fn3">3</a></sup> instruction that will jump directly to the correct block. It is to your advantage to use complex instructions.</p>
<h3 id="loop-optimizations">Loop Optimizations</h3>
<p>With these general ideas in mind, we can begin to explore more advanced optimization techniques. When optimizing bytecodes, our primary goal is to reduce the total instruction count to a bare minimum. We&rsquo;re lucky in that we don&rsquo;t have to benchmark to determine performance &ndash; we only have to count the total number of instructions<sup id="fnref4"><a href="#fn4">4</a></sup>. The easiest way to illustrate optimization is to walk through a complete example of optimizing a tight loop.</p>
<p>Lets begin with a hypothetical controller class that encapsulates an array of objects that we care about, say <code>enemy_robots</code>. We want to build a method called <code>scanAll</code> that will iterate through all the enemy robots one by one and call the <code>scan</code> method on each.</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Controller</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">RobotInfo</span><span class="o">[]</span> <span class="n">enemy_robots</span><span class="o">;</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">scanAll</span><span class="o">()</span> <span class="o">{</span>
<span class="cm">/* code to iterate through enemey_robots and scan them */</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>Since Java 5, there has been an easy way to write these for-each loops, which will do nicely for our first pass:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">for</span><span class="o">(</span><span class="n">RobotInfo</span> <span class="n">rinfo</span> <span class="o">:</span> <span class="n">enemy_robots</span><span class="o">)</span> <span class="o">{</span>
<span class="n">rinfo</span><span class="o">.</span><span class="na">scan</span><span class="o">();</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>I&rsquo;ve done the following things to the below disassembly &ndash; I&rsquo;ve heavily annotated each opcode, and I&rsquo;ve also <em>renumbered</em> the indices given from <code>javap</code> from a byte-offset to an instruction index (as the instruction count is what we are penalized for).</p>
<div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre>aload_0 // Variable 0 (the "this" object reference)
getfield #14 // Field enemy_robots:[Lbytecodetests/RobotInfo;
dup // Duplicates the last object on the stack (enemy_robots)
astore 4 // locals[4] = enemy_robots
arraylength
istore_3 // locals[3] = enemy_robots.length
iconst_0 // loads the value 0 onto the stack
istore_2 // locals[2] = 0 (or the loop index)
goto 17 // LOOP BEGINS HERE:
aload 4
iload_2
aaload // loads index (locals[2]) of enemy_robots
astore_1 // locals[1] = enemy_robots[index]
aload_1
invokevirtual #21 // Method bytecodetests/RobotInfo.scan:()V
iinc 2, 1 // locals[2]++
iload_2
iload_3
if_icmplt 10 // if index &lt; enemyrobots.length, jump to instruction 10
</pre></td></tr></tbody></table></code></pre></div>
<p>In the above disassembly, the compiler has assigned the following variables into the locals array as such:</p>
<ol>
<li>variable <code>rinfo</code></li>
<li>implicit loop index</li>
<li><code>enemy_robots.length</code></li>
<li><code>enemy_robots</code></li>
</ol>
<p>The zeroth position is special &ndash; it&rsquo;s almost always <code>this</code>, that is, the current enclosing object. We&rsquo;ll see later why that is important.</p>
<p>Our main loop body is from instruction 10 to instruction 19, a total size of 10 bytecodes. So our total bytecode count for this routine is the overhead (12) plus the loop body (10) times the number of iterations. Assuming that the number of iterations is large, how can we reduce this cost? One way is to write the loop in a more old fashioned way:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">enemy_robots</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">enemy_robots</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">scan</span><span class="o">();</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>Note that we pre-compute <code>length</code> so that we don&rsquo;t incur the cost of computing <code>enemy_robots.length</code> every loop iteration (doing so would be an extra <code>aload</code>, <code>getfield</code> and <code>arraylength</code> per loop instead of a single <code>iload</code> call). The emitted byte code is below, again annotated and re-indexed:</p>
<div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre>aload_0
getfield #16 // Field enemy_robots:[Lbytecodetests/RobotInfo;
arraylength
istore_1
iconst_0
istore_2
goto 14 // LOOP BEGINS HERE:
aload_0
getfield #16 // Field enemy_robots:[Lbytecodetests/RobotInfo;
iload_2
aaload
invokevirtual #23 // Method bytecodetests/RobotInfo.scan:()V
iinc 2, 1
iload_2
iload_1
if_icmplt 8
</pre></td></tr></tbody></table></code></pre></div>
<p>In this example, the compiler has actually only assigned three local variables:</p>
<ul>
<li>locals[0]: the <code>this</code> reference</li>
<li>locals[1]: the precomputed array-length <code>length</code></li>
<li>locals[2]: the loop index <code>i</code></li>
</ul>
<p>The total loop comes out to 9 bytecodes per iteration. We saved exactly 1 bytecode for increased code complexity. Sadistic, isn&rsquo;t it? The saved instruction comes from not having to <code>astore</code>, <code>aload</code> the extra local variable <code>rinfo</code> that was required in the for-each example. We did however lose a bytecode having to <code>getfield</code> the implicit class-variable <code>enemey_robots</code>. Lets try to recover it.</p>
<h3 id="pulling-things-into-local-scope">Pulling things into local scope</h3>
<p>In our above example, because <code>enemy_robots</code> is actually a class-level variable, in order to reference <code>enemy_robots</code>, the implicit <code>this</code> must be pushed onto the stack first.</p>
<p>Each access thus requires an <code>aload_0</code> followed by a <code>getfield</code>. If we instead assign <code>enemy_robots</code> to a local variable, it becomes a single bytecode <code>aload #x</code>, grabbed directly from local variable array. So let&rsquo;s pay the overhead cost to bring <code>enemy_robots</code> down into local scope and rewrite the loop:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">RobotInfo</span><span class="o">[]</span> <span class="n">local_enemy_robots</span> <span class="o">=</span> <span class="n">enemy_robots</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">local_enemy_robots</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="n">local_enemy_robots</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">scan</span><span class="o">();</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>Again, for additional complexity, we save another bytecode!<sup id="fnref5"><a href="#fn5">5</a></sup> Can we do even better?</p>
<h3 id="comparisons-against-zero">Comparisons against zero</h3>
<p>It turns out we can actually save one additional bytecode if we rewrite the entire loop structure as follows:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="n">local_enemy_robots</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="o">--</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">;)</span> <span class="o">{</span>
<span class="n">local_enemy_robots</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">scan</span><span class="o">();</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div><div class="highlight"><pre class="highlight plaintext"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre>aload_1
arraylength
istore_2
goto 9
aload_1
iload_2
aaload
invokevirtual #32 // Method bytecodetests/RobotInfo.scan:()V
iinc 2, -1
iload_2
ifge 5
</pre></td></tr></tbody></table></code></pre></div>
<p>Because the loop now decrements from the array length to zero, our loop termination conditional is a check against zero, which java has a special bytecode for: <code>ifge</code>. This means that we only have to push one number onto the stack instead of the two required for <code>if_icmplt</code>, cutting out an <code>iload</code>. This now brings us down to 7 bytecodes!</p>
<h3 id="putting-it-all-together">Putting it all together</h3>
<p>The following table shows the loop bodies of each step of our optimization with the extraneous instruction bolded:</p>
<table><thead>
<tr>
<th style="text-align: right"></th>
<th style="text-align: left">for-each</th>
<th style="text-align: left">for-index</th>
<th style="text-align: left">with-locals</th>
<th style="text-align: left">reversed</th>
</tr>
</thead><tbody>
<tr>
<td style="text-align: right">1</td>
<td style="text-align: left">aload</td>
<td style="text-align: left">aload_0</td>
<td style="text-align: left">aload_1</td>
<td style="text-align: left">aload_1</td>
</tr>
<tr>
<td style="text-align: right">2</td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left"><strong>getfield</strong></td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left">iload_2</td>
</tr>
<tr>
<td style="text-align: right">3</td>
<td style="text-align: left">aaload</td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left">aaload</td>
<td style="text-align: left">aaload</td>
</tr>
<tr>
<td style="text-align: right">4</td>
<td style="text-align: left"><strong>astore_1</strong></td>
<td style="text-align: left">aaload</td>
<td style="text-align: left">invokevirtual</td>
<td style="text-align: left">invokevirtual</td>
</tr>
<tr>
<td style="text-align: right">5</td>
<td style="text-align: left"><strong>aload_1</strong></td>
<td style="text-align: left">invokevirtual</td>
<td style="text-align: left">iinc</td>
<td style="text-align: left">iinc</td>
</tr>
<tr>
<td style="text-align: right">6</td>
<td style="text-align: left">invokevirtual</td>
<td style="text-align: left">iinc</td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left">iload_2</td>
</tr>
<tr>
<td style="text-align: right">7</td>
<td style="text-align: left">iinc</td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left"><strong>iload_1</strong></td>
<td style="text-align: left">ifge</td>
</tr>
<tr>
<td style="text-align: right">8</td>
<td style="text-align: left">iload_2</td>
<td style="text-align: left">iload_1</td>
<td style="text-align: left">if_icmplt</td>
<td style="text-align: left"></td>
</tr>
<tr>
<td style="text-align: right">9</td>
<td style="text-align: left">iload_3</td>
<td style="text-align: left">if_icmplt</td>
<td style="text-align: left"></td>
<td style="text-align: left"></td>
</tr>
<tr>
<td style="text-align: right">10</td>
<td style="text-align: left">if_icmplt</td>
<td style="text-align: left"></td>
<td style="text-align: left"></td>
<td style="text-align: left"></td>
</tr>
</tbody></table>
<p>To recap, just by tweaking and reorganizing the structure of the loop itself, we managed to reduce overhead instruction count by 30%. And in tight loops that run hundreds/thousands of times, we bank appreciable bytecode savings that we then can spend on more critical code paths &ndash; like running a pathfinding algorithm several steps further, or processing a few more enemies in a weapons targeting system.</p>
<h3 id="generating-gotos">Generating GOTOs</h3>
<p>Any discussion of loop optimization wouldn&rsquo;t be complete without a brief discussion of loop termination. In Java, similarly to most languages, you can early terminate a loop with the <code>break</code> keyword, or skip a loop iteration with the <code>continue</code> keyword. It&rsquo;s the closest thing we have in Java to a general purpose <code>goto</code> statement.</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">while</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span><span class="o">(</span><span class="n">condition_one</span><span class="o">)</span> <span class="k">continue</span><span class="o">;</span>
<span class="k">if</span><span class="o">(</span><span class="n">condition_two</span><span class="o">)</span> <span class="k">break</span><span class="o">;</span>
<span class="cm">/* code */</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>By putting those two conditional checks early, we can force the compiler to generate a <code>goto</code> instruction and prevent wasteful execution of the loop body.</p>
<p>In Java, you can also break out of two nested loops by labeling the first loop and using a labeled break statement as follows:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nl">outerloop:</span> <span class="k">while</span><span class="o">(</span><span class="n">first_condition</span><span class="o">)</span> <span class="o">{</span>
<span class="k">while</span><span class="o">(</span><span class="n">second_condition</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span><span class="o">(</span><span class="n">third_condition</span><span class="o">)</span> <span class="k">break</span> <span class="n">outer</span> <span class="n">loop</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>This neat trick can help prevent the need for a sentinel value in the outer loop. A corresponding trick that most people <em>don&rsquo;t</em> know is that you can actually break out of <em>arbitrary</em> labeled blocks. This gives you an ugly but capable forward-jumping &ldquo;goto&rdquo; statement:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="nl">label1:</span> <span class="o">{</span>
<span class="nl">label2:</span> <span class="o">{</span>
<span class="cm">/* code */</span>
<span class="k">if</span><span class="o">(</span><span class="n">first_conditional</span><span class="o">)</span> <span class="k">break</span> <span class="n">label2</span><span class="o">;</span>
<span class="k">if</span><span class="o">(</span><span class="n">second_conditional</span><span class="o">)</span> <span class="k">break</span> <span class="n">label1</span><span class="o">;</span>
<span class="o">}</span>
<span class="cm">/* more code */</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div>
<p>As a disclaimer, I would never, ever, ever use this in normal day-to-day code, but if you need to squeeze some extra bytecodes in a pinch, it&rsquo;ll do.</p>
<h3 id="closing-thoughts">Closing Thoughts</h3>
<p>My hope is that this post has given you some insight into how the Java compiler emits bytecodes and how you can use that to your advantage to reduce your total instruction count.</p>
<p>In the 2012 Battlecode competition, we used the above techniques extensively in writing what we called the <a href="https://bitbucket.org/Cixelyn/bcode2012-bot/src/69758a5c59a00545923a99f914c580898d6aa88e/teams/ducks/HibernationSystem.java">hibernation system</a>, a very tight loop that consumed only 69 bytecodes per turn. In that year&rsquo;s game spec, an AI&rsquo;s unused bytecodes could be directly refunded for energy at the end of the turn, so the hibernation system was effectively a low-power state for our AIs that allowed us to stockpile energy. This, in turn, allowed our army to sustain roughly 2x more robots than normally possible with typical (1000-2000) bytecode usage, giving us the edge in combat.</p>
<p>It can&rsquo;t be stressed enough, however, that many of these optimizations should be done only <em>after</em> all other avenues have been exhausted. There are a large number of algorithmic and data structure tricks to perform which may yield even greater savings, some of which are discussed in our <a href="https://bitbucket.org/Cixelyn/bcode2012-bot/downloads/strategyreport.pdf">winning 2012 strategy report</a>. I hope to write up these as a stand-alone post at some point, as they are somewhat out of the scope of this article. But for the curious, our strategy report, combined with <a href="http://stevearc.blogspot.com/2011/12/code-snippits_17.html">Steve Arcangeli&rsquo;s 2011 code snippet notes</a>, should provide a reasonable background to the topic.</p>
<p>To the teams competing in the 2014 competition: best of luck, and don&rsquo;t forget to have fun!</p>
<div class="footnotes">
<hr>
<ol>
<li id="fn1">
<p>When looking through Java disassembly, it&rsquo;s helpful to have a quick reference. Wikipedia has page on <a href="http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings">Java byte codes and their operations</a> which is useful for at-a-glance lookup. The more detailed (and official) description of the bytecode operations can be found in <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html">Oracle&rsquo;s JVM reference</a>.&nbsp;<a href="#fnref1">&#8617;</a></p>
</li>
<li id="fn2">
<p>When reading the output of <code>javap</code>, the comments following an <code>invokevirtual</code> command denote the signature of the method being invoked. The format is the list of arguments types in parenthesis followed by the return value type. The types are shortened to their one letter code to remain compact.&nbsp;<a href="#fnref2">&#8617;</a></p>
<table><thead>
<tr>
<th>Source Declaration</th>
<th>Method Descriptor</th>
</tr>
</thead><tbody>
<tr>
<td><code>void m(int i, float f)</code></td>
<td><code>(IF)V</code></td>
</tr>
<tr>
<td><code>int[] m(int i, String s)</code></td>
<td><code>(ILjava/lang/String;)[I</code></td>
</tr>
</tbody></table>
</li>
<li id="fn3">
<p>As an aside, for speed, the Bytecode format actually has two types of table-based jumps: <code>lookupswitch</code> and <code>tableswitch</code>. If the indices are roughly sequential, the compiler will pack them into a <code>lookupswitch</code> table in which the parameter into the switch statement is the table offset, giving an O(1) lookup of the jump address. If the indices are far apart / non-sequential however, packing them into a fixed-interval table would be very wasteful, and so the <code>lookupswitch</code> table stores both the case value and the jump offset, allowing the JVM to binary search through the possible case statements for the correct jump vector.&nbsp;<a href="#fnref3">&#8617;</a></p>
<p>The best thing of course is that despite <code>lookupswitch</code> having O(1) complexity and <code>tableswitch</code> having O(log <em>n</em>) complexity, in battlecode they&rsquo;re equivalent because we&rsquo;re only counting the bytecodes, and not the true computational cost! So you don&rsquo;t have to worry about creating compact case statements! :D</p>
</li>
<li id="fn4">
<p>That&rsquo;s not to say benchmarking isn&rsquo;t important, as it&rsquo;s often one of the fastest ways to profile new routines or survey where your biggest bytecode expenses are. The engine conveniently provides <code>Clock.getBytecodeNum()</code> to check your usage for the current turn.&nbsp;<a href="#fnref4">&#8617;</a></p>
<p>If you&rsquo;re benchmarking large routines or your entire AI framework, you&rsquo;ll want to make sure you account for bytecode overage due to turn-skipping. In our <a href="https://bitbucket.org/Cixelyn/bcode2012-bot/src/69758a5c59a00545923a99f914c580898d6aa88e/teams/ducks/BaseRobot.java?at=default#cl-220">main framework</a>, one of the first things we wrote was a method to get true bytecode count so we could accurately gauge performance. The following formula should give the correct count:</p>
<div class="highlight"><pre class="highlight java"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="kt">int</span> <span class="n">byteCount</span> <span class="o">=</span>
<span class="o">(</span><span class="n">GameConstants</span><span class="o">.</span><span class="na">BYTECODE_LIMIT</span><span class="o">-</span><span class="n">executeStartByte</span><span class="o">)</span> <span class="o">+</span>
<span class="o">(</span><span class="n">currRound</span><span class="o">-</span><span class="n">executeStartTime</span><span class="o">-</span><span class="mi">1</span><span class="o">)</span> <span class="o">*</span> <span class="n">GameConstants</span><span class="o">.</span><span class="na">BYTECODE_LIMIT</span> <span class="o">+</span>
<span class="n">Clock</span><span class="o">.</span><span class="na">getBytecodeNum</span><span class="o">();</span>
</pre></td></tr></tbody></table></code></pre></div></li>
<li id="fn5">
<p>In this example, we only access <code>enemy_robots</code> a single time in the loop body &ndash; in a real-world example, this technique has the potential to realize even greater savings, especially for member variables that are accessed often.&nbsp;<a href="#fnref5">&#8617;</a></p>
</li>
</ol>
</div>
</content>
</entry>
<entry>
<title>Battlecode: MIT's longest-running hardcore programming competition</title>
<link rel="alternate" href="/battlecode-intro/"/>
<id>/battlecode-intro/</id>
<published>2013-01-07T00:00:00-08:00</published>
<updated>2013-01-07T00:00:00-08:00</updated>
<author>
<name>Cory Li</name>
</author>
<summary type="html"><p>As MIT’s <a href="http://web.mit.edu/iap/">Independent Activities Period</a> draws near, I’ve received quite a volume of inquiries from underclassmen about “what should I take?” or “which of these competitions is better?”</p>