tag:blogger.com,1999:blog-5486137186367396362024-03-18T21:38:15.578-04:00Paul Gestwicki's Bloga blog for reflective practice that was cleverly named after its authorPaul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.comBlogger602125tag:blogger.com,1999:blog-548613718636739636.post-88566125989717648772024-03-09T13:47:00.004-05:002024-03-09T13:47:57.883-05:00Painting ISS Vanguard<p>It has been a long time since I finished a full set of miniatures. I have been painting off and on, but it hasn't been complete sets. For example, my sons and I have painted a few <i><a href="https://boardgamegeek.com/boardgame/295770/frosthaven">Frosthaven</a></i> and <i><a href="https://boardgamegeek.com/boardgame/251661/oathsworn-deepwood">Oathsworn</a> </i>figures but not all of them. I have some <i><a href="https://boardgamegeek.com/boardgame/315610/massive-darkness-2-hellscape">Massive Darkness 2</a></i> figures primed that have been sitting on a box awaiting inspiration for some time. </p><p>In any case, I'm here to break the streak with <i><a href="https://boardgamegeek.com/boardgame/325494/iss-vanguard">ISS Vanguard</a></i>. My older boys and I really enjoyed <i>Etherfields</i>. It came with a promotional comic about <i>ISS Vanguard</i> that piqued my interest. Somehow, I heard about how <a href="https://awakenrealms.com/">Awaken Realms</a> had re-opened the pledge manager for the game between Wave 1 and Wave 2, so I jumped in. I received my copy many weeks ago, but I knew I did not want to bust it open until we finished <i>Oathsworn</i>. We are now two chapters away from doing so, and so I have finished this core set of figures just in time.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxm17Umq_MxIn6jxFFe1UPkz56ZO6ruewcuufJ50E50rgbCcaIb2qMWJnss7khfiUs98UEW5pxi0TSgsJ-7KJHiNI8r6jh_cuMYtGxNUXNj21H4tjoM5B0QOQ6CZimPEJpMAZXLMo4NKxxsL0XUL0TRui9KaIONuUvYZA0is9vTNS0vC9ZvU7cgqyj3A/s698/pic7245199.webp" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="600" data-original-width="698" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxm17Umq_MxIn6jxFFe1UPkz56ZO6ruewcuufJ50E50rgbCcaIb2qMWJnss7khfiUs98UEW5pxi0TSgsJ-7KJHiNI8r6jh_cuMYtGxNUXNj21H4tjoM5B0QOQ6CZimPEJpMAZXLMo4NKxxsL0XUL0TRui9KaIONuUvYZA0is9vTNS0vC9ZvU7cgqyj3A/s320/pic7245199.webp" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><i>ISS Vanguard</i> Box Art</td></tr></tbody></table><p>Most of the things I paint are based on concept art, and I like to match the colors of the figures to the artwork. This is especially important in board games so that figures are easily distinguishable. The eight human figures in ISS Vanguard don't have any published concept art, however, so I had to come up with a different way to handle them. Awaken Realms offers a service where they "sundrop" miniatures, which essentially means that they are given a high-quality wash. Their approach makes it clear that the figures are in pairs, two for each of the four playable sections: security, recon, science, and engineering. I liked the idea of painting them in matching pairs, and searching for inspiration online revealed that many others painters did as well. <a href="https://www.reddit.com/r/ISSVanguard/comments/y39dly/finished_painting_my_iss_vanguard_crew/">This Reddit post</a> was my favorite, where the painter featured the distinct section colors on each model but otherwise used a high-contrast scheme with white armor and dark detailing. Coincidentally, a friend shared <a href="https://youtu.be/ghAUcuYRW0s">a video</a> on Facebook last night called, "White plastic and blinking lights: the sci fi toys of the late 1970s and early 1980s." I didn't watch the whole thing, but it did get me reflecting on <i>why</i> I believed that white armor with bright colors should match a science fiction setting.</p><p>The figures were slightly frustrating to paint. There are many fiddly details on the armor, but not all of it is meaningful. It seems like the kind of thing that would look great when sundropping because that leaves it as monochromatic detail without having to be specific about what pieces logically or thematically connect. I used the aforementioned Reddit post regularly to try to plan out where I wanted splashes of color. There are a few parts that I would consider recoloring if I had the paints on hand, but a lot of the colors were custom mixes; it wasn't worth the risk of having a bad match to recolor it.</p><p>I used zenithal priming from the airbrush to prep the figures. I then painted all of them with a slightly warm off-white color, mostly white with a dot of grey and of buff. I used a wash over the whole figure to darken the recesses, then hit the highlights with the off-white armor color.</p><p>Let's look at the figures in pairs.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWemQh9PotwCnEP-duLpWTd3mE1b5Jvv0DZTPTxcnlLGbZIiuWDiExAxuxyOzHkEfPNyAk7vH-KxkmwrMk_6j6SsbeZIPwb3RjzIkD2Ay3bABzksq0saBO-HhTNSOVm7nvzcR266zlv-vK2bIlXcAeeBhcCsM_S2_OjrkvLi1f5Pu78423zP_fe3MVhA/s1200/PXL_20240309_133738562.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1200" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWemQh9PotwCnEP-duLpWTd3mE1b5Jvv0DZTPTxcnlLGbZIiuWDiExAxuxyOzHkEfPNyAk7vH-KxkmwrMk_6j6SsbeZIPwb3RjzIkD2Ay3bABzksq0saBO-HhTNSOVm7nvzcR266zlv-vK2bIlXcAeeBhcCsM_S2_OjrkvLi1f5Pu78423zP_fe3MVhA/s320/PXL_20240309_133738562.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Engineering Section</td></tr></tbody></table><p>All miniature painters know that yellow is a challenging color. Fortunately, a white undercoat made it manageable. The one on the left could probably use slightly more yellow, but I do like how it looks in isolation, and one will never have both of these out in the same mission anyway.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpMWVx9tEVZHh9iVhv2oN2pqvjDtxb2f4JDE2062J-h2dL2tQEruz_u-5NHtpIvNzdLoAsTA2mjwzD2MheezMNfnaOHDDN8-SEs0-3Q3E2-7wLdKd8XcoZb63UvKkZK3cgc2JVVtBjjF-aYLwE8JPoLxqbIFf1GeBuh5FiRN3ExmtqdXQNkgltJqWcow/s1232/PXL_20240309_133606333.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1232" data-original-width="1232" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpMWVx9tEVZHh9iVhv2oN2pqvjDtxb2f4JDE2062J-h2dL2tQEruz_u-5NHtpIvNzdLoAsTA2mjwzD2MheezMNfnaOHDDN8-SEs0-3Q3E2-7wLdKd8XcoZb63UvKkZK3cgc2JVVtBjjF-aYLwE8JPoLxqbIFf1GeBuh5FiRN3ExmtqdXQNkgltJqWcow/s320/PXL_20240309_133606333.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Security Section</td></tr></tbody></table><p>I thought a lot about whether the little "pet" on the left should be white like the armor or a different color for contrast. I ended up keeping it white to suggest that it's made of the same stuff as the bulk of the armor. </p><p>I like the poses of these two. These figures all make good use of scenic bases. Part of me prefers blank bases, since I can then decide whether or not I want to add features and suggest that the characters are in particular settings, as I did most elaborately in my <i>Temple of Elemental Evil</i> set... whose images sadly seems to have been eaten by a grue, in a horrible example of why you should not trust "the cloud." Here, however, we can see that a pose like that recon figure on the right would really not be possible any other way. The engagement with the scenery makes it worthwhile in a way that those engineering figures feel more like it's in the way. </p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxvhDfNx131x6uYCRuPuJNaqEkVgDplObTi376e_nWtPrnomuW6Q71P7ehCH-AWuMuwifEj6yS9U7188tezmIGsc9czZLihv9m-ZjmKBFy44Le9PeWMrANfeixSormSEZxL9DOXaIkMQFq2ZrGYSWHSGoubWDTJfWeNPChLt_o23HSkHCTKPePBKdLcA/s1224/PXL_20240309_133519685.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1224" data-original-width="1224" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxvhDfNx131x6uYCRuPuJNaqEkVgDplObTi376e_nWtPrnomuW6Q71P7ehCH-AWuMuwifEj6yS9U7188tezmIGsc9czZLihv9m-ZjmKBFy44Le9PeWMrANfeixSormSEZxL9DOXaIkMQFq2ZrGYSWHSGoubWDTJfWeNPChLt_o23HSkHCTKPePBKdLcA/s320/PXL_20240309_133519685.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Recon Section</td></tr></tbody></table><p>The Recon Section also has wonderful, dynamic poses. I was worried that the smokey jet trail of the one on the right might be too much, but I think it turned out fine. I chose yellow for the flowery thing on the left figure in part to complement the dark blue of the strap and mask details and partially so that it has similar colors to the jetpack character</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPpU5_EhTTspoxe4TyF8381_Nk82qk_XF9zlLqgZaWAdamrzmfYWCV7aAP2hmhh8jq5Qf8RS9Q__0IaI9mv2j7ZRqeImup9e7VFBr1LzbCDdHreZRUaub8_ndHVtKdbzEP7Cf1d3qPfhGaVPorU-57GEi5JMLQsdvzAbf_q-25nIP0aLgYidLkcFt6eA/s1263/PXL_20240309_133654427.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1262" data-original-width="1263" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPpU5_EhTTspoxe4TyF8381_Nk82qk_XF9zlLqgZaWAdamrzmfYWCV7aAP2hmhh8jq5Qf8RS9Q__0IaI9mv2j7ZRqeImup9e7VFBr1LzbCDdHreZRUaub8_ndHVtKdbzEP7Cf1d3qPfhGaVPorU-57GEi5JMLQsdvzAbf_q-25nIP0aLgYidLkcFt6eA/s320/PXL_20240309_133654427.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Science Section</td></tr></tbody></table><p>The yellow figures may have taken the most time because of the troubles getting yellow to be bright enough, but the Science Section was awfully close because of all the stuff in their scenes. I had some similar thoughts about the claw arm as the security section's pet, and I ended up going the same way here: if white plastic is what they're using to build lightweight rigid armor, then let's use it for the claw arm and the pets, too.</p><p>The alien biomatter being picked up by the one on the right looked fungous, so I picked out some colors inspired by that. Of course, a giant mushroom here on earth would not also have green leaves sticking out of it. </p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA3fcTEytCiLUScy-h8YCEp3Oe57AAXafUxYZWgBlmOzKp6IPjTry0JFsZ2RzvIBjlYs7om9qUFlvrpsjC25oyTIRTVw1JbZztGw5_eXLKH5ZUi_yzldAlgQCwwdGEmkWPCtMqCdJgPEIODcVdsxtr-AkF3-7sC5jMSXp9J9PgwKhJ2Nv6ShmvNljyaw/s1539/PXL_20240309_124808667.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1154" data-original-width="1539" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA3fcTEytCiLUScy-h8YCEp3Oe57AAXafUxYZWgBlmOzKp6IPjTry0JFsZ2RzvIBjlYs7om9qUFlvrpsjC25oyTIRTVw1JbZztGw5_eXLKH5ZUi_yzldAlgQCwwdGEmkWPCtMqCdJgPEIODcVdsxtr-AkF3-7sC5jMSXp9J9PgwKhJ2Nv6ShmvNljyaw/s320/PXL_20240309_124808667.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">ISS Vanguard (?)</td></tr></tbody></table><p>The last figure in the box is a big space station. I presume it is the titular ISS Vanguard, but the parts of the rulebook that I have read don't actually reference it at all. It's not clear to me if this is used in play or not. I wish it did, though, since that would have given me some idea of how much effort I should spend painting it.</p><p>As with the human characters, I looked around online and found a few ideas for painting this piece. I kept the "nearly white with spots of color" motif. One of the challenges here is that the way a space station would be lit is quite different from how an away team would be, but I didn't want to paint it so starkly. I ended up using a cold off-white here to differentiate it subtly from the warm off-white of the characters. A wash deepened some of the recesses, then some highlights and spot colors. It's fine. I waffled a bit on whether to just paint over the silly translucent bit, but I chose against it in part because I have no idea if it is significant to the story. Who knows, maybe the campaign plot hinges on understanding that people are using pure translucent blue as a power source? I wanted the blue to match the beautiful tone used on the box cover, but I didn't quite get it. It's not purple enough, but it does match the translucent parts.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSNg33Ynx28Vc3euIMzEj8kgAkTNFZi5BBrKznf9d9NOa5gWfg3uZMXkkBm8234jdfAuNhJQW4TLnnvM92bG6coEW1GYr1ICOQduQOZOetpVJ-DmnodvWqgEK2hh5-41OB16rX77thTlJzWqwAAaOh4ILNIJRitMzAuusURCBpkCmato_QaRglXnGPjw/s1553/PXL_20240309_134046034.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="377" data-original-width="1553" height="98" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSNg33Ynx28Vc3euIMzEj8kgAkTNFZi5BBrKznf9d9NOa5gWfg3uZMXkkBm8234jdfAuNhJQW4TLnnvM92bG6coEW1GYr1ICOQduQOZOetpVJ-DmnodvWqgEK2hh5-41OB16rX77thTlJzWqwAAaOh4ILNIJRitMzAuusURCBpkCmato_QaRglXnGPjw/w400-h98/PXL_20240309_134046034.jpg" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">All Eight Characters</td></tr></tbody></table><p>Thanks for checking out the photos and the story here. I'll include some more individual pictures below for people who want to see more detail, including the backs. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7imQA1l1GIvIDrpnobzPXZ80kY1fRjOOBE0n9zxRFOJWNIK_rIklz8c4FHaG202PBZyVK3PxAAAo27b-y10svjCZ6_JRlbVZrdKo5VRBLLkpzt78qjl5ayIJG0oN86zTEjLSyybUSAjCcgubNVvgqPWSfDY8w9KISSL6v1A2yitX_2NF9iE5JL7vVxw/s1725/PXL_20240309_124756400.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1293" data-original-width="1725" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7imQA1l1GIvIDrpnobzPXZ80kY1fRjOOBE0n9zxRFOJWNIK_rIklz8c4FHaG202PBZyVK3PxAAAo27b-y10svjCZ6_JRlbVZrdKo5VRBLLkpzt78qjl5ayIJG0oN86zTEjLSyybUSAjCcgubNVvgqPWSfDY8w9KISSL6v1A2yitX_2NF9iE5JL7vVxw/s320/PXL_20240309_124756400.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrBYpe6zFrWmyr8JxsIZACQzSqkIOrCh7TERSKyiPdODt_OV_88f2lVPQLf5P0Qgf2DiR2LFgXxhzJn9ZdbUZmTZT_fBc_NDxzRF8PSQhO1DcoD3fMpPo1j_fgomnmxdGqf2i_ID-RHM6JfLpIND18sd_VVF6AHMMtowellDmUziWQ1yo2lc8yosAVKA/s984/PXL_20240309_124733824.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="984" data-original-width="738" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrBYpe6zFrWmyr8JxsIZACQzSqkIOrCh7TERSKyiPdODt_OV_88f2lVPQLf5P0Qgf2DiR2LFgXxhzJn9ZdbUZmTZT_fBc_NDxzRF8PSQhO1DcoD3fMpPo1j_fgomnmxdGqf2i_ID-RHM6JfLpIND18sd_VVF6AHMMtowellDmUziWQ1yo2lc8yosAVKA/s320/PXL_20240309_124733824.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiAJIKGIoTqOT_LC1hCz-2dIyKIFhbsA4AWIzzswJpTxjyX6GR1iqQTmrkYBtxtkqn64zGhZZagxvAhYH-vOzSunugNfvsr1hxK69WkNUxOMbp5r7t227fGTnFPugYTryTCM3UYxNYFn3avHoJerwkoZ-SW2Ndv87nYCAFtL5cfxCg6cLSqzRjT0NfWw/s1023/PXL_20240309_124741671.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1023" data-original-width="767" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiAJIKGIoTqOT_LC1hCz-2dIyKIFhbsA4AWIzzswJpTxjyX6GR1iqQTmrkYBtxtkqn64zGhZZagxvAhYH-vOzSunugNfvsr1hxK69WkNUxOMbp5r7t227fGTnFPugYTryTCM3UYxNYFn3avHoJerwkoZ-SW2Ndv87nYCAFtL5cfxCg6cLSqzRjT0NfWw/s320/PXL_20240309_124741671.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEkj5d1oMeul9KZTE6IThRzqkH9XE_mm_wva7lL31V-xzcqUHXP4tSZEaCd5GLgD49XPOdmDll6Zy-JVlz6GQBwHEA3nzmWE3lzAJngqrntWsavg2Ov9LH_qBE9h6Y5-qu2zSeg2cDGvvGHw2G6zPpBIBJesgXL2YLiXuX2FojJIYsL4VFc5I0PZ4AhQ/s1142/PXL_20240309_124715082.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1142" data-original-width="857" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEkj5d1oMeul9KZTE6IThRzqkH9XE_mm_wva7lL31V-xzcqUHXP4tSZEaCd5GLgD49XPOdmDll6Zy-JVlz6GQBwHEA3nzmWE3lzAJngqrntWsavg2Ov9LH_qBE9h6Y5-qu2zSeg2cDGvvGHw2G6zPpBIBJesgXL2YLiXuX2FojJIYsL4VFc5I0PZ4AhQ/s320/PXL_20240309_124715082.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivP_eOtmNu4wrrUoe_D728z1UUp_AjkRWYv2tTaSOvLAKbGRASQwL18lGP9IzFoKhezLnSJD_DJRim_G-1qyigtG7AQiaNnPS-SJ6fhpmPTPO2jop1HV6-TlOvNMsI5uV5RT-smSKi682qRIEhdkni5wIMvlHfamR13Bx0EN8Y9tQKUIskr-6bvJZhCg/s1150/PXL_20240309_124721478.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1150" data-original-width="863" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivP_eOtmNu4wrrUoe_D728z1UUp_AjkRWYv2tTaSOvLAKbGRASQwL18lGP9IzFoKhezLnSJD_DJRim_G-1qyigtG7AQiaNnPS-SJ6fhpmPTPO2jop1HV6-TlOvNMsI5uV5RT-smSKi682qRIEhdkni5wIMvlHfamR13Bx0EN8Y9tQKUIskr-6bvJZhCg/s320/PXL_20240309_124721478.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyLCDd8wR3wBcwb8pqdY-wM8d-V6AQHAjo3hsAJbeKBk-nEBdCdLVr4ksz7k3VT0eMlgeASFmNja4mYyrjSqJZqCGAy6AHzOV3tcDP_YTuj1OiQ4_u5DSC1KAHmCQF7x_5EEGvlXWJO-ncyvqykqDGDdsnEhXAl16rswgF4IoQE6oqEdi56hGaahUsGg/s1010/PXL_20240309_124654852.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1010" data-original-width="757" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyLCDd8wR3wBcwb8pqdY-wM8d-V6AQHAjo3hsAJbeKBk-nEBdCdLVr4ksz7k3VT0eMlgeASFmNja4mYyrjSqJZqCGAy6AHzOV3tcDP_YTuj1OiQ4_u5DSC1KAHmCQF7x_5EEGvlXWJO-ncyvqykqDGDdsnEhXAl16rswgF4IoQE6oqEdi56hGaahUsGg/s320/PXL_20240309_124654852.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBHgYnYsOw-YLXWsfLfFuF1icYBVdhFhJ_wAr94lHopopyR3MGP4dKAoMMGtglvuf9GqBqRxrNK7NE4Vr3ZLnqcCpPG0kr9tgSNFe4T-kFkKff9thpN5RdamCwdFisP83uOFQ1QQklqCn9gatOPNbmESIxUXKNVXrwkLxvD6eUBjFI0ptDVrhm_-10yA/s1124/PXL_20240309_124700596.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1124" data-original-width="843" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBHgYnYsOw-YLXWsfLfFuF1icYBVdhFhJ_wAr94lHopopyR3MGP4dKAoMMGtglvuf9GqBqRxrNK7NE4Vr3ZLnqcCpPG0kr9tgSNFe4T-kFkKff9thpN5RdamCwdFisP83uOFQ1QQklqCn9gatOPNbmESIxUXKNVXrwkLxvD6eUBjFI0ptDVrhm_-10yA/s320/PXL_20240309_124700596.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqZZiNzyrEzvHWvccSZ9V1SVkGq43T_kG2rNkX_VrihTVYxoMjrHEkaeQ9LVjZ-xwo89rBqoSVT15vNpxbZA2TOGZxXS7bwoKB23K1zKxOBfLMQuOmMlb7_5xKiw5OchlXcsGrkrktRSFleSprawveummshUHmeZ9bsqHMlPhhmIi-Y03Na1sAiVr0yA/s1121/PXL_20240309_124635962.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1121" data-original-width="841" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqZZiNzyrEzvHWvccSZ9V1SVkGq43T_kG2rNkX_VrihTVYxoMjrHEkaeQ9LVjZ-xwo89rBqoSVT15vNpxbZA2TOGZxXS7bwoKB23K1zKxOBfLMQuOmMlb7_5xKiw5OchlXcsGrkrktRSFleSprawveummshUHmeZ9bsqHMlPhhmIi-Y03Na1sAiVr0yA/s320/PXL_20240309_124635962.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTYvT9bSbXPNxG952ejGAfV63EKyOf12K7lLgM9_le6q7ntN47ILSQvYREf_TQsuSsWy5kMnL3qfXIAznEyW_ywPQRmh5Du4yp_YtP6PCQQRUoAjFyaI83ia23OgHDPMjxvOUwLWlnPnVtjjmVzW29O9h67s7-71WB2PBGbotkFw2MT9OjfoR0LMiCuQ/s1166/PXL_20240309_124642950.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1166" data-original-width="875" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTYvT9bSbXPNxG952ejGAfV63EKyOf12K7lLgM9_le6q7ntN47ILSQvYREf_TQsuSsWy5kMnL3qfXIAznEyW_ywPQRmh5Du4yp_YtP6PCQQRUoAjFyaI83ia23OgHDPMjxvOUwLWlnPnVtjjmVzW29O9h67s7-71WB2PBGbotkFw2MT9OjfoR0LMiCuQ/s320/PXL_20240309_124642950.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCvaF_HuO8-Q6KRAQiTBWzusB3Z_FnY6J_IHZTBTqzuUTa8Kyg9z3QoJOFoeGoAKFcBcNI7XjsnROjKhMMMPt2_Ahn9zgEIXAxcz5Ix0-QWmqawikhL66AZ-3l-E4acoQK_OWlmyC5G4dhso3ajnTJyOaWiBD5vCav1DfP9_GhhRqGYhEahGNZZOLDwg/s1055/PXL_20240309_124616536.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1055" data-original-width="791" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCvaF_HuO8-Q6KRAQiTBWzusB3Z_FnY6J_IHZTBTqzuUTa8Kyg9z3QoJOFoeGoAKFcBcNI7XjsnROjKhMMMPt2_Ahn9zgEIXAxcz5Ix0-QWmqawikhL66AZ-3l-E4acoQK_OWlmyC5G4dhso3ajnTJyOaWiBD5vCav1DfP9_GhhRqGYhEahGNZZOLDwg/s320/PXL_20240309_124616536.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAYszmiICooEJoOf7K0YmhC7_qi5aEG4VOPejVtS6CLJZhwe4Mtd0__tSZzzaNUBSThQH1-x6oNA-o7JaJrWkcQAfQJ5gdv5zNVJokJea0VA7EO_rQMsRce07_qrYvsZtg30UrBkbGYPpawIuDP2SK3ppETuiybO5YTxXNHXLp0kVOkIFegY_C4KYuDQ/s1070/PXL_20240309_124623349.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1070" data-original-width="802" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAYszmiICooEJoOf7K0YmhC7_qi5aEG4VOPejVtS6CLJZhwe4Mtd0__tSZzzaNUBSThQH1-x6oNA-o7JaJrWkcQAfQJ5gdv5zNVJokJea0VA7EO_rQMsRce07_qrYvsZtg30UrBkbGYPpawIuDP2SK3ppETuiybO5YTxXNHXLp0kVOkIFegY_C4KYuDQ/s320/PXL_20240309_124623349.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjciV2BRSEHvE_8rDmSD-W3zXUD1ezT2JOdRenBATdmV_HykHKAg4sTumJK48Y2hXn2EN71b0UI7vFZ75Ry_UBzw2ueNgpgEqW1kKbmCj2Kds6ITRcafIRSG6g5UTKY2EVSLLKjocunLUMjpX7MUUPDrtI9tO0l9hpOencztfwP0TekC4ZiweyLZcFaWA/s910/PXL_20240309_124557469.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="910" data-original-width="683" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjciV2BRSEHvE_8rDmSD-W3zXUD1ezT2JOdRenBATdmV_HykHKAg4sTumJK48Y2hXn2EN71b0UI7vFZ75Ry_UBzw2ueNgpgEqW1kKbmCj2Kds6ITRcafIRSG6g5UTKY2EVSLLKjocunLUMjpX7MUUPDrtI9tO0l9hpOencztfwP0TekC4ZiweyLZcFaWA/s320/PXL_20240309_124557469.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBfIfafivXrsLHq4WjwtPv_BNnJ2VVVML2GctpZjRupCr6SOgloUtFoZjzU_BLDs11dhgaHHNpN_E7uB7-R_h6W1khpIWFIYl5vAUbHABzMKn_35xQ8RzsfybKFk_-AtzNRk0CKWBAYo3c-Xzo1mALgK50YX0V78yTT33mvTQM8AfI2PSTHVA4ppty7Q/s914/PXL_20240309_124603255.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="914" data-original-width="685" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBfIfafivXrsLHq4WjwtPv_BNnJ2VVVML2GctpZjRupCr6SOgloUtFoZjzU_BLDs11dhgaHHNpN_E7uB7-R_h6W1khpIWFIYl5vAUbHABzMKn_35xQ8RzsfybKFk_-AtzNRk0CKWBAYo3c-Xzo1mALgK50YX0V78yTT33mvTQM8AfI2PSTHVA4ppty7Q/s320/PXL_20240309_124603255.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiySyHweuY6qlDMAzMhoXLNkSu-xkaaC-5BTqDjwJywovUsHbKGjrUJdBF42UeiLifcxP6PyAj01qIi2ztWkO2Vxsuj0StwAyyAel9xgN3KdSauq8Dcp03Ekt8_5IXDv4Mg_nD8QuBUT5UVPFmwQ5Tqg1HhW1Uti78b9nsZX9J1cMjayl49EC-nZX7naA/s992/PXL_20240309_124537909.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="992" data-original-width="744" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiySyHweuY6qlDMAzMhoXLNkSu-xkaaC-5BTqDjwJywovUsHbKGjrUJdBF42UeiLifcxP6PyAj01qIi2ztWkO2Vxsuj0StwAyyAel9xgN3KdSauq8Dcp03Ekt8_5IXDv4Mg_nD8QuBUT5UVPFmwQ5Tqg1HhW1Uti78b9nsZX9J1cMjayl49EC-nZX7naA/s320/PXL_20240309_124537909.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfIcr-X9d8MKSfsCp6bMARr-SRHwE0WI4a_TqP9u2cshUD_j5YXIJHXo_y_GZ_d3V8-ZY_uX4HL3eG0nMrpg-siK0ozdb_uWyUsZPQwPEJPDrEOhU_VnfTTFSmqml8vM604VLyvTXTGLq2Y7fH6MTqGGbkmZgJbdgzDjskoYy3s26ewOaC9thD6-3urQ/s984/PXL_20240309_124545827.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="984" data-original-width="738" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfIcr-X9d8MKSfsCp6bMARr-SRHwE0WI4a_TqP9u2cshUD_j5YXIJHXo_y_GZ_d3V8-ZY_uX4HL3eG0nMrpg-siK0ozdb_uWyUsZPQwPEJPDrEOhU_VnfTTFSmqml8vM604VLyvTXTGLq2Y7fH6MTqGGbkmZgJbdgzDjskoYy3s26ewOaC9thD6-3urQ/s320/PXL_20240309_124545827.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk5kY0Xn_HSlIPssnYDWufXYQHOFai5RGrm2VPhQY_Lytj5qb8NE3nhi3gZfkIJF3tph9Ft-xYyHRxXatgH2MpVzwtNbJGuqeJWSEXzRaJShplIJJvm8SVlPDdtCeRWO5UKHPxpj37HL1OrljSRC6yhJvkeI6lNBuoJdUsyhqbaDGn2Ydchfhx5UciGQ/s1199/PXL_20240309_124518617.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1199" data-original-width="899" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk5kY0Xn_HSlIPssnYDWufXYQHOFai5RGrm2VPhQY_Lytj5qb8NE3nhi3gZfkIJF3tph9Ft-xYyHRxXatgH2MpVzwtNbJGuqeJWSEXzRaJShplIJJvm8SVlPDdtCeRWO5UKHPxpj37HL1OrljSRC6yhJvkeI6lNBuoJdUsyhqbaDGn2Ydchfhx5UciGQ/s320/PXL_20240309_124518617.jpg" width="240" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPGdNdngW5QK_Ma1QRSDI4nY09eSK7ytcgv4A4X4hjULG4SuLWR6iqY8YT7IKPd100Es8Zxu7c93Bv3SweD2gYhqRKnpjfLLab6xkiLepIWyMnbJkRy4F80d0pl07NjjWObxp0N9-HyRr_DHqWx4uC3Ry4UO-bDTJ6ketl4PvZijDj0gAUbDcs2dW9RQ/s1042/PXL_20240309_124526313.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1042" data-original-width="781" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPGdNdngW5QK_Ma1QRSDI4nY09eSK7ytcgv4A4X4hjULG4SuLWR6iqY8YT7IKPd100Es8Zxu7c93Bv3SweD2gYhqRKnpjfLLab6xkiLepIWyMnbJkRy4F80d0pl07NjjWObxp0N9-HyRr_DHqWx4uC3Ry4UO-bDTJ6ketl4PvZijDj0gAUbDcs2dW9RQ/s320/PXL_20240309_124526313.jpg" width="240" /></a></div><br /><p><br /></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com2tag:blogger.com,1999:blog-548613718636739636.post-44402099049123963792024-02-15T10:07:00.003-05:002024-02-15T10:07:17.140-05:00Reaping the benefits of automated integration testing in game developmentThis academic year, I am working on a research and development project: <a href="https://github.com/STEM-Careers-Game/stem_career_paths">a game to teach middle-school and early high-school youth about paths to STEM careers</a>. I have a small team, and we are funded by the Indiana Space Grant Consortium. It's been a rewarding project that I hope to write more about later.<div><br /></div><div>In the game, the player goes through four years of high school, interacting with a small cast of characters. We are designing narrative events based on real and fictional stories around how people get interested in STEM. Here is an example of how the project looked this morning:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaJhX6wu-FtN5rT14tJ2SCB8pMjD3Tj0vBYxfyHDNbdYkf6Ry91Jxe3QCCTt9LPAqN6b-rtrU-oLzORPv9zOnpSa8hBs7zMANQVJIODR7wswsxb_DIWgRuQEAWvkkictKTTe-iyFXa8VcL5O_EpyJ7U7xfShyphenhyphenWvXOCL07LKOWJs17Qjm489yrhH49CIQ/s1055/sample.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1055" data-original-width="760" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaJhX6wu-FtN5rT14tJ2SCB8pMjD3Tj0vBYxfyHDNbdYkf6Ry91Jxe3QCCTt9LPAqN6b-rtrU-oLzORPv9zOnpSa8hBs7zMANQVJIODR7wswsxb_DIWgRuQEAWvkkictKTTe-iyFXa8VcL5O_EpyJ7U7xfShyphenhyphenWvXOCL07LKOWJs17Qjm489yrhH49CIQ/s320/sample.png" width="231" /></a></div><div><br /></div><div>This vignette is defined by a script that encodes all of the text, the options the player has, the options' effects, and whether the encounter is specific to a character, location, and year. </div><div><br /></div><div>We settled on the overall look and feel several months ago, and in that discussion, we recognized that there was a danger in the design: if the number of lines of text in the options buttons (in the lower right) was too high, the UI would break down. That is, we needed to be sure that none of the stories ever had so many options, or too much text, that the buttons wouldn't fit in their allocated space.</div><div><br /></div><div>The team already had integration tests configured to ensure that the scripts were formatted correctly. For example, our game engine expects narrative elements to be either strings or arrays of strings, so we have a test that ensures this is the case. The tests are run as pre-commit hooks as well as on the CI server before a build. My original suggestion was to develop a heuristic that would tell us if the text was likely too long, but my student research assistant took a different tack: he used <a href="https://github.com/bitwes/Gut">our unit testing framework's</a> ability to test the actual in-game layout to ensure that no story's text would overrun our allocated space.</div><div><br /></div><div>In yesterday's meeting, the team's art specialist pointed out that the bottom-left corner of the UI would look better if the inner blue panel were rounded. She mentioned that doing so would also require moving the player stats panel up and over a little so that it didn't poke the rounded corner. I knew how to do this, so I worked on it this morning. It's a small and worthwhile improvement: a cleaner UI with just a little bit of configuration. </div><div><br /></div><div>I ran the game locally to make sure it looked right, and it did. Satisfied with my contribution, I typed up my commit message and then was surprised to see the tests fail. How could that be, when I had not changed any logic of the program? Looking at the output, I saw that it was the line-length integration test that had failed, specifically on the "skip math" story. I loaded that one up to take a look. Sure enough, the 10-pixel change in the stat block's position had changed the line-wrapping in this one particular story. Here's how it looked:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJEpDXNZTy22yUpxsTC76JWDV71SNtjul_ymRukrZAa0tyUEx-9OMHINo7jd-C0u1xJXkEbbFcETh_tY6K0FovGx_JlXBingAVYyXqQOdWU4kbxVbo8Hawir1shph87nMqAYJAGSXmylHiwNUN6DUE1-9VRCy6k4J9fbPZa7_G0fTIYffGPA2tWT4qng/s1055/math_bad.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1055" data-original-width="760" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJEpDXNZTy22yUpxsTC76JWDV71SNtjul_ymRukrZAa0tyUEx-9OMHINo7jd-C0u1xJXkEbbFcETh_tY6K0FovGx_JlXBingAVYyXqQOdWU4kbxVbo8Hawir1shph87nMqAYJAGSXmylHiwNUN6DUE1-9VRCy6k4J9fbPZa7_G0fTIYffGPA2tWT4qng/s320/math_bad.png" width="231" /></a></div><br /><div>Notice how the stat block is no longer formatted correctly: it has been stretched vertically because the white buttons next to it have exceeded their allocated space. </div><div><br /></div><div>This is an unmitigated win for automated testing. Who knows if or when we would have found this defect by manual testing? We have a major event coming up on Monday where we will be demonstrating the game, and it would have been embarrassing to have this come up then. Not only does this show the benefit of automated testing, it also is a humbling story of how my heuristic approach likely would not have caught this error, but the student's more rigorous approach did.</div><div><br /></div><div>I tweaked the "skip math" story text, and you can see the result below. This particular story can come from any character in any location, and so this time, it's Steven in the cafeteria instead of Hilda in the classroom.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhmQTjporX9UJdAb3NQ6dSIkJ1gyxjY1szGxE0zu7RESaDcV7Xt1PIEk4owJL6I6cWs6cfpNTS_Oq0x6P24zJZFi_ywGWZYSVLmCQj-3FDMU-gobW8itMq8KCCwdLKQBjv3SXxMI1pnkhoVnIfxPFTA14VerNi2-6mIbMuzgOVRrRslt2QzUDf6ay4Q/s1055/math_good.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1055" data-original-width="760" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhmQTjporX9UJdAb3NQ6dSIkJ1gyxjY1szGxE0zu7RESaDcV7Xt1PIEk4owJL6I6cWs6cfpNTS_Oq0x6P24zJZFi_ywGWZYSVLmCQj-3FDMU-gobW8itMq8KCCwdLKQBjv3SXxMI1pnkhoVnIfxPFTA14VerNi2-6mIbMuzgOVRrRslt2QzUDf6ay4Q/s320/math_good.png" width="231" /></a></div><br /><div>We will be formally launching the project before the end of the semester. It will be free, open source, and playable in the browser.</div>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com2tag:blogger.com,1999:blog-548613718636739636.post-36721146129254454852024-02-09T13:47:00.003-05:002024-02-09T13:47:18.049-05:00Tales of Preproduction: Refining the prototyping procedure<p>I am teaching the Game Preproduction class for the second time this semester, and this time I am joined by <a href="https://www.bsu.edu/academics/collegesanddepartments/art/about-us/faculty-and-staff/faculty/sandersantonio">Antonio Sanders</a> as a team-teacher from the School of Art. There are already a lot of interesting things happening as we have a class that is now have Computer Science majors and half Animation majors. We have also extended the class time to a "studio" duration, so we meet twice a week for three hours per meeting instead of the 75 minutes I had with my inaugural group last year.</p><p>Given that quick summary of our context, I want to share a significant change that we made to <a href="https://paulgestwicki.blogspot.com/2023/02/notes-from-inaugural-game-preproduction_18.html">the prototyping process from last year</a>. Last year, the team adjusted the schedule because we didn't dedicate enough ideation time to prototyping, and so this year, we set aside five days for this. Each day, the students are supposed to bring in a prototype that answers a design question. I remember this also being challenging last year, and it wasn't until late in that process that we remembered the seven questions that Lemarchand poses about prototypes in <i>A Playful Production Process</i>. </p><p>In an effort to get the students thinking more critically about their prototypes, we have required them to write short prototype reports that address Lemarchand's seven questions. The last of the questions, which Lemarchand himself typesets in bold to show its importance, is, "What question does this prototype answer?" What the reports help reveal, which was harder to see last year, were cases where the questions themselves were either malformed or unanswerable. That is, students are going into prototyping without a good idea of what prototyping is. Several times, I've seen students show their prototypes, and when I ask what design question they answer, the students have to look it up on their reports. This is pretty strong evidence that the questions were developed post hoc. What's most troubling is that, after having completed four of the planned five rounds, these problems are still rampant.</p><p>Early in the process, my teaching partner suggested students think about design questions in the form "Is <i>X Y?</i>" where <i>X</i> is a capability being prototyped and <i>Y</i> is a design goal. For example "Is holding the jump button down to fly giving the player a sense of freedom?" While this heuristic proved helpful, a lot of students struggled with it: in part, I think they didn't understand that it was only a heuristic, and in part because they haven't practiced the analysis skills required to pull a design question out of an inspiration. If I were to use this again, I'd follow the obvious-in-retrospect need to rename those variables, to something like "Does <i>this player action</i> produce <i>this design goal?</i>" (Unfortunately, the discussion of design goals comes up later in the book, so maybe even this idea is too fuzzy for the students.)</p><p>Many of the questions that students want to pursue are actually research questions. I mean this in both the colloquial and the academic senses. A question like, "Does adding a sudden sound make the player scared when they see the monster?" is obviously answered in the affirmative: one need only look at games that induce jump-scares to see that this is effective. Questions like, "Do timers increase player stress?" are simple design truisms that are not worth prototyping. In yesterday's class, I tried to explain to the students that if the question is <i>generic</i> then it's a research question, and that design questions are always about <i>specifics</i>. In particular, through science, we approach generic questions through specific experiments that attempt to answer the general question; through design, we answer specific questions through specifics directly.</p><p>Reflecting on these problems, it becomes clear that the earlier parts of the semester were not goal-directed enough. Students acknowledged after our in-class brainstorming session that they were not brainstorming game ideas (but that's a topic for another post). When the students did research, many of these were also not goal-directed. Now, in prototyping, we're more easily to see what students are interested in, and we can point out to them that their interests and issues can and should be solved by blue-sky ideation or by research. However, we haven't baked that into these first five weeks. Put another way, <b>we took a waterfall approach to ideation whereas perhaps next year we should try an iterative one</b>.</p><p>We're in the process of collecting summaries of all the students' prototypes. I put together a form that uses this template for students to self-describe prototypes that are viable for forming teams around:</p><p><i>This game will be a </i>GENRE/TYPE<i> where the player </i>CORE MECHANISM<i> to </i>GOAL/THEME. </p><p>I'm eager to see if this was a helpful hook for the students. I will have to ask them about it on Tuesday and then see if it's something we can use with next year's cohort.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-522735741798680872024-01-02T16:17:00.001-05:002024-01-02T16:17:11.250-05:00Notes from "Grading for Growth"<p>I read portions of <i><a href="https://www.taylorfrancis.com/books/mono/10.4324/9781003445043/grading-growth-linda-nilson-david-clark-robert-talbert">Grading for Growth</a></i> as part of my preparations for the Spring's preproduction class. This book makes a case for "alternative grading," establishing four pillars for their efforts. These are:</p><p></p><ol style="text-align: left;"><li>Clearly defined standards</li><li>Helpful feedback</li><li>Marks indicate progress</li><li>Reattempts without penalty</li></ol><div>I've been reading <a href="https://rtalbert.org/">Talbert's blog</a> for some time, and it's that last one that gives me some difficulty. I was hoping that reading the book would help me understand some practical matters such as grading management and dealing with skills that build upon each other. However, I found myself taking more notes about CS222 Advanced Programming and CS315 Game Programming than about CS390 Game Studio Preproduction.</div><div><br /></div><div>I have read Nilson's <i>Specification Grading</i> and many articles on alternative grading, so I skipped through some of the content and case studies. The first case study is the one that was most relevant to me: a case of a calculus class in which the professor used standards-based grading (SBG). This was contributed by <a href="https://seaver.pepperdine.edu/academics/faculty/joshua-bowman/">Joshua Bowman at Pepperdine University</a>.</div><div><br /></div><div>One of the tools that I had not fully considered before is the <i>gateway exam</i>. Bowman gives a ten-question exam early in the semester. Students must pass at least nine questions to pass the exam. Students get five chances to retake the exam, and a passing grade is required for a B- or better grade. This is potentially useful to deal with some of the particular problems I have faced in CS222, where students come in with high variation in understanding of programming fundamentals while also suffering from second-order ignorance. A formalized assessment could very well help with this. </div><div><br /></div><div>Another useful idea from the reading is the distinction between <i>revision</i> and <i>new attempt</i>. In my own teaching, I have allowed revisions, but I frequently in CS222 find myself suggesting that students begin assignments anew with new code or contexts. This was never a clear requirement but rather a strong suggestion. Separating these two ideas could increase clarity about the significance of error or misunderstanding. In particular, this could help with a particular error mode that I have seen in CS222: a student submits a source code evaluation, I critique the evaluation, and the student resubmits an evaluation that restates what I just pointed out in the critique. This masks the distinction between a student who has learned the material and one who can effectively parrot my commentary. The problem could be avoided if I required new attempts in cases where I am using my feedback to direct the student's attention to what they have missed rather than to point out small oversights.</div><div><br /></div><div>Regular readers may recall that I experimented with specifications-based grading in my section of CS222 in Fall 2023. I only laid out cases for A, B, C, D, and F grades, similarly to how I have implemented specs grading in CS315. The reading suggested that +/- grades can also be laid out in a specification, using them for "in between" cases.</div><div><br /></div><div>I regularly air my frustrations with the incoherent concept of "mid-semester grades," but a piece of advice from the book struck me as useful. There was a recommendation to only give A, C, or F grades at midsemester. This is probably the right level of granularity for the task. The alternative, which I also recently came across in a blog somewhere, was to have students write their own mid-semester evaluations as a reflective exercise.</div><div><br /></div><div>Bowman and others separate their standards into <i>core</i> and <i>auxiliary</i>. This could be useful in both CS222 and CS315, where I tend to weave together content that students are required to know from the syllabus with those that I think are useful from my experience.</div><div><br /></div><div>The authors directly address the problem that reassessments have to be meaningful. Unlimited resubmissions will inevitably lead to students' throwing mediocre attempts at the problem in hopes that it goes away. The authors suggest two techniques for ensuring assessments are meaningful. The first is to gate the possibility for reassessment behind meaningful practice, which probably works better in courses with more objective content such as mathematics courses. The other is to require a reflective cover sheet. I have required students to give memos explaining the resubmission, but I've never given them a format for what this entails. This has led to my accepting many "memos" that show little evidence of understanding, usually when my patience is exhausted. Formalizing the memo process would benefit everyone involved.</div><div><br /></div><div>Those are all helpful ideas for this summer, when I will likely take elements of CS222 and CS315 back to the drawing board, but what about the resubmission rate issue that I was actually looking for? Well, I found quite a surprise. The authors suggest exactly what I have been doing for years: using a token-based system or throttling resubmissions. The real puzzle here then is what exactly they mean by "reattempts without penalty," since it's not what those words actually mean together. Only being able to reattempt a subset of substandard assignments <i>is</i> a penalty, since from a pure learning point of view, there's no essential reason to prevent it. That is, the penalty is coming from the practical matter that teachers cannot afford to teach every student as if they are their only responsibility. This finding was anticlimactic, but part of me expected that it would be what I had found. There's no silver bullet, and if I haven't seen nor invented something better in 20+ years of alternative grading experience, then it does not exist.</div><div><br /></div><div>(It's funny to actually type out "20+ years of alternative grading experience," but it's true. It's also one of those things that's making me feel old lately.)</div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-13393287793711097392024-01-01T14:02:00.002-05:002024-01-01T14:02:32.109-05:00The Games of 2023<p>In 2023, I logged 408 plays of 71 different board games. I am surprised how much lower that is than the last two years, but I think it also points to playing more heavy games rather than multiple light games. My youngest son is almost nine, and he will join in any game that we invite him. Just this morning, we rung in the new year by playing <i>Massive Darkness 2</i>, and he did great with one of the most complicated character classes. We can probably unload some of the kids old games and to make room for... well, honestly, the games we already have that just don't have a home on a shelf.</p><p>Here are the games that I played at least ten times this past year:</p><p></p><ul style="text-align: left;"><li><i>Frosthaven </i>(54)</li><li><i>Clank! Catacombs</i> (32)</li><li><i>Railroad Ink</i> (26)</li><li><i>Everdell</i> (19)</li><li><i>Res Arcana</i> (16)</li><li><i>Terraforming Mars: Ares Expedition</i> (16)</li><li><i>Oathsworn: Into the Deepwood</i> (12)</li><li><i>Cribbage</i> (11)</li><li><i>So Clover!</i> (11)</li><li><i>Ark Nova</i> (10)</li><li><i>Thunderstone Quest</i> (10)</li></ul><div>We haven't had <i>Frosthaven</i> to the table in months, so it was a shock for me to see it so strongly in the top. My son and I have played through practically all the main storyline, though we have not unlocked all the characters. I am a little disappointed that, after the main quests are done, there's not much pull to go back into the game. It's not like the game mechanisms changed much, but once there's no narrative hook to move forward, it just stopped feeling like it mattered if we collected the materials to build the buildings to get more materials to save a settlement that was actually fine.</div><div><br /></div><div><i>Cribbage</i> is a game I played a lot as a kid and watched my parents play with their friends. It's a comforting game. A glass of wine and a game with my wife always makes for a good evening.</div><div><br /></div><div>I did not think we played <i>Ark Nova</i> as much last year as we did. Maybe that expansion is in our future.</div><div><br /></div><div>As of the start of the year, my game h-index is 33, meaning that there are 33 games that I have played at least 33 times. This will certainly go up this year, as it seems I only need one more play of <i>Castles of Mad King Ludwig</i> to increase to 34, and that's a game I love to play. My player h-index is 19, meaning that there are 19 players with whom I have played at least 19 games. This one seems much harder to increase! </div><div><br /></div><div>I'll conclude by sharing the most-played games in my collection, continuing a tradition for this blog series.</div><div><ul style="text-align: left;"><li><i>Race for the Galaxy</i> (112)</li><li><i>Clank</i> (102)</li><li><i>Thunderstone Quest</i> (102)</li><li><i>Crokinole</i> (88)</li><li><i>Kingdomino</i> (82)</li><li><i>My City</i> (67)</li><li><i>Gloomhaven</i> (66)</li><li><i>The Quacks of Quedlinburg</i> (65)</li><li><i>Arcadia Quest</i> (61)</li><li><i>Frosthaven</i> (61)</li><li><i>Carcassonne</i> (60)</li><li><i>Animal Upon Animal</i> (56)</li><li><i>Quiddler</i> (56)</li><li><i>Camel Up</i> (51)</li><li><i>Terraforming Mars: Ares Expedition</i> (47)</li><li><i>Rhino Hero: Super Battle</i> (43)</li><li><i>Cribbage</i> (41)</li><li><i>The Crew</i> (40)</li><li><i>Just One</i> (40)</li><li><i>Mage Knight Board Game</i> (40)</li><li><i>Runebound Third Edition</i> (40)</li></ul><div>It may look like <i>Race</i> wins, but if we tally together <i>Clank</i>, <i>Clank Legacy</i>, and <i>Clank Catacombs</i>, the <i>Clank</i> family dominates with 158 total plays (102+17+39, respectively).</div></div><div><br /></div><div>Thanks for reading. Happy New Year and Happy Gaming!</div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-231372462301248942023-12-20T10:05:00.003-05:002023-12-20T10:05:51.160-05:00Reflecting on CS222 Advanced Programming, Fall 2023 Edition<p>As you may have noticed, I tried something a little different this year and extracted topical reflections into their own posts [<a href="https://paulgestwicki.blogspot.com/2023/12/the-conflict-of-individual-mastery.html">1</a>,<a href="https://paulgestwicki.blogspot.com/2023/12/on-ethics-of-obscurity.html">2</a>] rather than embed them into a lengthier reflection about a class. Aside from those concerns already expressed, CS222 went quite smoothly this semester. I had a small section, and I feel like I had a good rapport with the students. </p><p>I had most recently been teaching the course on a MWF schedule, but this semester it was back to my preferred Tuesday-Thursday schedule. This means more time in one session to dive into a topic, but it also meant I touched on fewer topics. This is a worthwhile exchange, but I didn't get to all the extras I like to cover during the semester. For example, we didn't get a chance to explore state management in Flutter as much as I would have liked. That particular context is where we get into the Observer design pattern, which this batch of students will not know by name. I also did not get a chance to talk at all about software licensing and intellectual property aside from a quick, hand-waving statement that the students own the rights to their projects.</p><p>I also added a fourth week to the pre-project portion of the class, cutting a week off of the final project to compensate. This gave more time in the early part of the semester, where students tend to struggle with the basics. Shortening the iteration lengths for the final project did have the anticipated positive effect that students worked more consistently. That is, reducing the time between deliverables gave students fewer opportunities to procrastinate.</p><p>The most surprising finding this semester was that the first Clean Code assignment was too easy. I've been giving this assignment for years: read the first chapter of Clean Code and write a paragraph reflecting on which definition of "clean code" most resonates with you. It is intended as a warm-up exercise to get students used to the unconventional method of documenting and submitting work for the course. One of my students pointed out that it gave him a false sense of what to expect from assignments, all of which take orders of magnitude more effort than this first one. I am thinking of simply dropping the assignment in favor of more meaningful ones.</p><p>I teach CS222 almost every semester, but I have a break next semester while I work on a funded research project. It will be good to have a little break from it, and I imagine I will be back on rotation some time next academic year. We also had a new faculty member teach the course this Fall, but I haven't made the opportunity to talk to him about the experience yet. I will do that in Spring.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-47491285841087510612023-12-19T14:46:00.000-05:002023-12-19T14:46:35.635-05:00On the ethics of obscurity<p>Years ago, I experimented with what is now called "specifications grading" in CS222. I set up a table that explained to a student how their performance in each category would affect their final grade. These are not weighted averages of columns but declarations of minima. For example, to get an A may require earning at least a B in all assignments, an A on the final project, and a passing grade on the exam. This gave a clarity to the students that was lacking when using more traditional weighted averages. While publishing weighted average formulae for students technically makes it possible for them to compute their grade for themselves, in practice, I have rarely or never found a student willing to do that level of work. Hence, weighted averages, even public ones, leave grades obscure to the students, whereas specification tables make grades obvious.</p><p>What my experiment found was specifications grading made students work less than weighted averages. The simple reason for this is that if a student sees that their work in one category has capped their final grade, they have no material nor extrinsic (that is, grade-related) reason to work in other columns. Using the example above, if a student earns a C on an assignment and can no longer earn an A in the class, they see that they may as well just get a B in the final project, too, since an A would not affect their final grade.</p><p>This semester in CS222, I decided to try specifications-based final grades again. It probably does not surprise you, dear reader, that I got the same result: students lost motivation to do their best in the final project because their poor performance on another part of the class. It's worse than that, though: the final project is completed by teams, and some team members were striving for and could still earn top marks while other team members had this door closed to them. That's a bad situation, and I am grateful for the students who candidly shared the frustration this caused them.</p><p>The fact is that students can and do get themselves into this situation with weighted averages as well. A student's performance in individual assignments may have doomed them to a low grade in the class despite their performance on the final project, for example. However, as I already pointed out, this is obscured to them because of their unwillingness to do the math. What this means—and I have seen it countless times—is that students will continue to work on what they have in front of them in futile hope that it will earn them a better grade in the course.</p><p><i>And that's a good thing</i>.</p><p>The student's ends may be unattainable, but the means will still produce learning. That is, the student will be engaged in the actual important part of the class. </p><p>Good teaching is about encouraging students to learn. That is why one might have readings, assignments, projects, quizzes, and community partners: these things help engage students in processes that result in learning. It is a poor teacher whose goal is to help students get <i>grades</i> rather than to help them <i>learn</i>. Indeed, every teacher who has endeavored to understand the science of learning at all knows that extrinsic rewards destroy intrinsic motivation. </p><p>What are the ethical considerations of choosing between a clear grading policy that yields less student learning and an obscure one that yields more? It seems to me that if learning is the goal, then there is no choice here at all. How far can one take this—how much of grading can we remove without damaging the necessary feedback loops? This is the essential question pursued by the ungrading movement, which I need to explore more thoroughly. </p><p>I also wonder, why exactly haven't we professors banded together and refused to participate in grading systems that destroy intrinsic motivation? </p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-42510004691683585262023-12-19T13:09:00.004-05:002023-12-19T13:09:45.638-05:00The Conflict of Individual Mastery Learning and Team Projects<p>I am uncertain how to optimally balance the desires for <i>mastery learning</i> and <i>teamwork</i>. This is not a new problem, but a conversation with a friend this week helped me articulate the particular pressures. I hope that summarizing the problem here will both give me practice explaining the tensions and foster conversation toward solutions.</p><p>Mastery Learning has students do work until it is done correctly. In theory, this is one the simplest and best ways to ensure that students gain the benefit we intend from assigned work. I have used a form of mastery learning for years in CS222. Students can resubmit assignments all the way through the end of the semester in order to show that they have learned from them. From very early in my experiments with mastery learning, I imposed a throttle on how many resubmissions students can use per week. This serves two purposes: it prevents students from dumping piles of work on me all at once, and because it reduces the number of evaluations I have to do at any time, it minimizes the time it takes me to give feedback to the students. I have a hard time imagining how advocates of "pure mastery learning," with any number of resubmissions allowed at any time, manage this.</p><p>I am an advocate of teamwork in undergraduate education with an important caveat that teams should only be given work that requires a team to accomplish. That is, if the work can be done by one person, it will be done by one person. There is a challenge here, where teams may not recognize how much work is actually required. That is why learning how to set scope and communicate is an implicit learning objective in almost every course I teach. I expect there to be some struggles here, the kind necessary for learning.</p><p>Mastery learning and teamwork come into conflict in my classes. Ideally, students should learn the skills and dispositions required before joining a team and working on a project together. The best way to ensure that all students learn that material is mastery learning, but with mastery learning, I cannot know when students will have completed all the work. One option is to impose an additional deadline before the project, but this seems counter to mastery learning: what would a student do after that deadline who has not mastered the requisite material? Another option is to gate the final project behind completion of the requisite material, but then teams would form in a staggered way. It is hard enough to get teams to form that can schedule sufficient out-of-class meetings to succeed, and adding any more impediments to this seems troublesome at best. An alternative way around this is to convert all the courses into studio courses, where there is no excuse for not being able to schedule time because it's built into the schedule. That comes with a significant cost to me: as much as I love studio classes, one has to recognize that they take twice as much of my time without a commensurate release of responsibilities elsewhere.</p><p>I am not sure whether traditional assignments or portfolios make a big difference in terms of this conundrum. For example, I could have students get into team projects right away, and by the end of the semester, turn in a portfolio that demonstrates their having individually met the learning objectives. This runs into the same problem as traditional resubmission of assignments, that students could put off working on the portfolios until much later than when that knowledge would have been helpful on the team projects.</p><p>The options I have sorted out seem to be the following.</p><p></p><ul style="text-align: left;"><li>Allow students to join team projects before they have shown mastery of the requisite material, continuing to allow resubmission of individual work while projects are ongoing.</li><li>Allow mastery learning resubmissions up until team projects start, or some other deadline before the end of the semester. A student's grade on those individual elements would be fixed at this point similarly to how they are at the end of a semester.</li><li>Gate team projects behind completion of mastery learning exercises: students can only join a team once they have shown that they have individually learned what they need to know. </li><li>Separate the courses entirely: require a particular grade in a course that is all about prerequisite knowledge in order to get into a course where teams apply and build on that knowledge together.</li></ul><div>Now that I write out that last one, it makes me realize I don't have a good answer as to why the deadline for mastery learning assignments is the end of the semester. That seems to be the tail wagging the dog. Isn't the end of the semester also an entirely arbitrary deadline? Perhaps that gives some credence to the second bullet above that I had not given it before.</div><div><br /></div><div>I fear I'm looking for a silver bullet. In the absence of such a thing, I am curious how other faculty balance these issues.</div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com3tag:blogger.com,1999:blog-548613718636739636.post-9995574139499929712023-12-13T14:40:00.001-05:002023-12-13T14:40:15.112-05:00Reflecting on CS315 Game Programming, Fall 2023 Edition<p>Game Programming is such a fun course to teach. In the best of cases, I get to bring interested students along with me on a journey through the fundamentals of the craft. This semester, I had several such students who clearly understood what we were doing, why we did it, and that it was interesting. These students were great, and I hope I take nothing from them by focusing on areas of the course that disappointed me. The challenge is always to keep what works and change what doesn't.</p><h3 style="text-align: left;">Checklists</h3><p>The first topic to explore is checklist-based grading. Game Programming is the course where I pioneered this approach, and I have generally been keen on it. However, I have never had so many cases of students failing to follow protocol as I have this semester. No matter how many times I reminded, remonstrated, encouraged, or commanded, a significant number of students checked items that were incomplete. </p><p>I wish had more data to distinguish between cases where students thought they had items that they didn't vs. cases where students were either lazy or trying to get away with something. The idea of Save Points<span style="background-color: white; color: #202124; font-family: Roboto, arial, sans-serif; font-size: 14px;">—w</span>here students get an opportunity to resubmit work and address one of the missed criteria<span style="background-color: white; color: #202124; font-family: Roboto, arial, sans-serif; font-size: 14px;">—</span>is for the former, not the latter. Responsible students do not struggle with this: they make mistakes and fix them. The mystery comes from irresponsible students. </p><p>My lovely wife made a suggestion to me just the other day based on her observation that there is no penalty to the students for checking all the boxes and forcing me to deal with it. There's a solution, then: impose a penalty on checklist errors. I wonder if that one simple fix will help students see that I am serious about checklists being their responsibility, that they should not check an item that they are sure is satisfied. The impact of honest mistakes could be softened by being able to use a limited number of Save Points on such errors.</p><p>I also wonder about the mode of checklists. I wrote before about the complications of having the checklists in version control within the project (although I cannot find it right now to link to it). I suspect part of the problem that irresponsible students run into is that they download the checklist and start marking things, and possibly the project changes afterward. For multi-iteration projects, I am sure that students just keep the checklists in place and do not re-evaluate them. I could circumvent these problems by requiring that checklists be completed on paper. That is, I could make it easy for students to download printable checklists, and then have them turn these in at the deadline. It would be extra paperwork to manage, but it would be worth it if it helps them understand the gravitas of checking a box.</p><h3 style="text-align: left;">Scoping and Slicing</h3><div>I feel like I had more students struggle with how to slice game programming problems this year than in the past. It's a hard problem! I do have them submit project plans to me, which I comment on, but it's not clear to me that they are acting on my feedback. That is, I may point out that their plans for a user story are too big, for example, but this doesn't seem to stop them. As above, it's hard to distinguish between whether they don't know how or aren't taking the feedback. </div><div><br /></div><div>It is not clear to me what I could do to improve this aside from give them more practice at this. At some point, it may simply be a combination of scale, difficulty, and lack of experience. That is, with this many students, doing something that is legitimately hard, without knowing that it's hard, it leaves me in a difficult position. Put another way, one way out of the problem is to declare it instead as a learning objective. This is potentially useful as this course, which used to be terminal, now leads into our preproduction class in the Game Design & Development concentration.</div><div><br /></div><div>However, it's also possible that there may be other cognitive tools I can teach the students to help them understand the difference between a half-baked cake and a half tray of cupcakes. Too few students come out of CS222 with an internalized understanding of the agile approach despite the use of incremental and iterative development there. </div><div><br /></div><div>One way to help them with this would be to spend more time together. That is, we could take a page out of the College of Fine Arts playbook and make this a studio class, with double contact hours. I've been teaching studio courses like this in Computer Science for many years. I don't think my colleagues are aware of it, which is only relevant when you're actually spending twice the contact hours with a class than otherwise. At the risk of stating the obvious, <i>it's twice as much time</i>. If we made Game Programming a studio course, I would have twice as much time to work alongside with students and help them out. The cost is that I would essentially give up half a day a week to the endeavor. I still struggle to see exactly where the optimum is on the cost-benefit curve here.</div><h3 style="text-align: left;">Engagement</h3><div>I'll close with an odd little story. The penultimate meeting of the class was my opportunity to introduce some topics of artificial intelligence to the class. Specifically, I talked about behavior trees, which is a topic for which I have had a group of students doing research with me this semester. This presentation had to start with some lecture about the material. Once I got the basics out of the way, I gave the students a challenge to try drawing a behavior tree for Tag. I told them to get into groups of three or four to see if they could sort out how to combine selectors, sequences, and actions to write a simple in-engine simulation. </div><div><br /></div><div>About half of the students clumped up into two groups and began approaching the problem, either on paper or at a whiteboard. The other half? Well, one of them looked like he tried to get the guy sitting next to him to talk, but he was rebuffed. The rest of them just sat there, faces into their laptops, exactly as they had been a moment before. This isn't the kind of course where I can just say "Laptops down" since we were working together in a demo repository, but clearly, there was a mismatch here of expectations. </div><div><br /></div><div>I was faced with a dilemma: should I call out the people who were disengaged or should I ignore them? I decided to simply ignore them. The day before the final presentations, I just cut off half of the room and stopped talking to them while the other half of the room and I explored the nifty space of behavior trees. I'm not exactly happy with this decision, but I think it was the right one. It may also be worth mentioning that a third of the class was not there, so really there were three categories, not two. It will surprise no one to hear that there's a near perfect correlation between the performance of students across the categories with respect to measurable effort and results. </div><div><br /></div><div>The problem is that this was just a clear symptom of the disengagement that had happened well before. Part of the issue is the room in which we had the class. It is a great space for studio work and a terrible place for interactive workshops. My usual room makes it easy for me to walk around and see what people are doing: it is practically or literally impossible in the room I was in. I leave this note here in part so that I can make sure next year that I am back in the teaching lab. I think if I had a more clear pattern of walking through and seeing what's on students' screen, I would not have had a whole side of the class physical and mentally far from me.</div>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-62452270481503993362023-12-08T07:13:00.002-05:002023-12-08T07:13:31.025-05:00Nine-year-old notes about The Burning Wheel<p>Opening up Facebook this morning, I found a "Facebook Memory" from nine years ago in which I shared my reaction to having read <i>The Burning Wheel</i>. I'm glad I read it, but the odds that I would ever find it again on Facebook are pretty low, so I am copying the post here.</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;">I finished reading <i>The Burning Wheel</i>, and it is a very interesting system. I hope to play a game of it someday, but even without playing, it was enlightening to read about this cult-favorite system. Thinking back on it, I think the most important piece is one of the most mundane elements, but something that can be brought into many interactive storytelling scenarios. When a player wants to act, he must identify his <i>intent</i> first, and only afterward to identify his <i>task</i>. The intent is the goal, the intended outcome, the reason for doing anything, and it ties into the systems' articulation of Beliefs. The task, then, is what the player actually does, and it is the task that is tested with dice and rules.</blockquote><br /><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><div style="text-align: left;">I can imagine how this would help a table of friends understand who the other characters are. In many settings, I have seen (or engaged in) players describing only what players are doing, but not why. The result is that other players have to guess at motives, infer what characters are about. This is the nature of social reality outside of the game, and it takes time to get to know people and their motives and beliefs. By making these explicit in the game, we model the idea that characters can get to know each other, and that we players are separate from them. That is, the players can get on with telling a good story because they understand more than just what is in their own character's head.</div></blockquote><div><div><br /></div></div><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><div><div style="text-align: left;">This works into another Burning Wheel rule, which is that the outcome of any die roll is articulated prior to the roll's being made. Hindsight is golden, but this strikes me as an essential rule for handling characters' social skills. Just as "hit the target with my sword" is the (potentially unstated) effect of succeeding at an attack roll, there should be a similar established context before a social roll. For example, "If you succeed, you convince the Duke to lend you his magic sword." Getting that explicit means that appropriate situation modifiers can be established. This, too, ties into BW's "Let it ride" rule, which says that once the dice have been cast, there's no do-overs in the same situation, whether it's social or combat. If you didn't convince the Duke, you didn't convince him---tell an interesting story about it and move on.</div></div></blockquote><p>I have not played this RPG in the intervening nine years. </p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-73324734880902265842023-12-07T16:32:00.001-05:002023-12-07T16:32:08.097-05:00What we learned in CS222, Fall 2023 edition<p>Once again, I gave my CS222 students the challenge to make a list of everything they learned this semester. This semester's group came up with 155 items, and then each individual voted on their top seven—the seven that they thought were the most important. Our top items this semester are as follows:</p><p></p><ul style="text-align: left;"><li>Clean Code (8 votes)</li><li>GitHub (7 votes)</li><li>TDD (5 votes)</li><li>Time Management (4 votes)</li><li>OOP (4 votes)</li></ul><div>If we had kept stepping down the line, the next cluster was at three votes, and only two items had these: Software Craftsmanship and Resubmissions. This is interesting to me, since I don't remember "resubmissions" being such a focus in past semesters on this list. However, a lot of students did a lot of resubmissions this semester. I don't fully understand why they would put this on the list and vote for it, but I am hopeful that it is because they have recognized the utility in thinking about learning this way: that it's not a case that you learn something or not and then move on, but that if it's worth learning, it will probably take some time to get it right.</div><div><br /></div><div>This really was a fun group of students in CS222 this semester. I feel like we had a good rapport, with many of the students catching on to the method behind the class. I think this manifests well in one of the items on the list today: a student said that they learned how to follow detailed system configuration instructions early in the semester, when configuring their Flutter development environment. I usually try not to interject while students are brainstorming their list of things they learned, but I did take the opportunity here to point out that <i>this was on purpose</i>, that part of the course design was to help them understand how to do this so that they would be better at it next time.</div><div><br /></div><div>I expect to write a more thorough retrospective on the semester next week, as I usually do. In the meantime, here is the full list that the students came up with, sorted by votes.<br /></div><div><br /></div><div><google-sheets-html-origin></google-sheets-html-origin></div><p></p><table border="1" cellpadding="0" cellspacing="0" data-sheets-root="1" dir="ltr" style="border-collapse: collapse; border: none; font-family: Arial; font-size: 10pt; table-layout: fixed; width: 0px;" xmlns="http://www.w3.org/1999/xhtml"><colgroup><col width="308"></col><col width="50"></col></colgroup><tbody><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Clean Code"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Clean Code</td><td data-sheets-value="{"1":3,"3":8}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">8</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"GitHub"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">GitHub</td><td data-sheets-value="{"1":3,"3":7}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">7</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"TDD"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">TDD</td><td data-sheets-value="{"1":3,"3":5}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">5</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Time Management"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Time Management</td><td data-sheets-value="{"1":3,"3":4}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">4</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"OOP"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">OOP</td><td data-sheets-value="{"1":3,"3":4}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">4</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Software Craftsmanship"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Software Craftsmanship</td><td data-sheets-value="{"1":3,"3":3}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">3</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Resubmissions"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Resubmissions</td><td data-sheets-value="{"1":3,"3":3}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">3</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"SRP"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">SRP</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Acceptance Testing"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Acceptance Testing</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Naming conventions"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Naming conventions</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Debugging"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Debugging</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Good names"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Good names</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Shu ha ri"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Shu ha ri</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Unit tests"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Unit tests</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Incremental Development"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Incremental Development</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Teamwork"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Teamwork</td><td data-sheets-value="{"1":3,"3":2}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">2</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Flutter"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Flutter</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"DRY"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">DRY</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Tokens"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Tokens</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Beck-Style Task List"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Beck-Style Task List</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"SMART"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">SMART</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Abstraction"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Abstraction</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Encapsulation"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Encapsulation</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Research"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Research</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"APIs"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">APIs</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Getters and Setters are Evil"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Getters and Setters are Evil</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Comments are evil"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Comments are evil</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Code should explain itself"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Code should explain itself</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Android Studio"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Android Studio</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Exploratory coding"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Exploratory coding</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Experimental code"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Experimental code</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Conditions of Satisfaction"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Conditions of Satisfaction</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Ctrl-Q"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Ctrl-Q</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Keyboard shortcuts"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Keyboard shortcuts</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Refactoring"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Refactoring</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Efficient programming"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Efficient programming</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Gamedev jobs/internships"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Gamedev jobs/internships</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Internships"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Internships</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Overcoming procrastination"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Overcoming procrastination</td><td data-sheets-value="{"1":3,"3":1}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; text-align: right; vertical-align: bottom;">1</td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Commit messages"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Commit messages</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Red-Green-Refactor"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Red-Green-Refactor</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"UI"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">UI</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Mob Programming"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Mob Programming</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"User Experience"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">User Experience</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"CRC Cards"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">CRC Cards</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"FIRST"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">FIRST</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"User Stories"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">User Stories</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Law of Demeter"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Law of Demeter</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Aliasing"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Aliasing</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"MVC"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">MVC</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Listeners"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Listeners</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Stepdown Rule"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Stepdown Rule</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Branching"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Branching</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Feature branching"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Feature branching</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Defensive Programming"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Defensive Programming</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Game Jams"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Game Jams</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Code Coverage"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Code Coverage</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Resume"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Resume</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Psycho/sociopaths on teams"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Psycho/sociopaths on teams</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Dart conventions"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Dart conventions</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Linked In"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Linked In</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Communication"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Communication</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"English"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">English</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Scheduling"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Scheduling</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Priority lists"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Priority lists</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"JSON decoding"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">JSON decoding</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"JSON"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">JSON</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"API Keys"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">API Keys</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Configuration files"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Configuration files</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Secure key management"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Secure key management</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Serialization"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Serialization</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Authorization"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Authorization</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Directoiry management"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Directoiry management</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Semantic Versioning"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Semantic Versioning</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Alan Kay"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Alan Kay</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"RCM"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">RCM</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Holub"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Holub</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Kent Beck"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Kent Beck</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Dijkstra"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Dijkstra</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"The Null Guy"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">The Null Guy</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Widgets"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Widgets</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Null Safety"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Null Safety</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Semantics"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Semantics</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Bureaucracy"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Bureaucracy</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Tim Cain videos"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Tim Cain videos</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Coding ethics"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Coding ethics</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Intellectual property"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Intellectual property</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Triangulation"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Triangulation</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Testing"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Testing</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Good use of types"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Good use of types</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Enumerated types"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Enumerated types</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Dart"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Dart</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Error handling"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Error handling</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Separation of concerns"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Separation of concerns</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Sorting"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Sorting</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Monads/Arity"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Monads/Arity</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Kanban boards"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Kanban boards</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Code review"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Code review</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Network clients"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Network clients</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Integration tests"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Integration tests</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Learning tests"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Learning tests</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Being programmed"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Being programmed</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Data structures"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Data structures</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Obvious implementation"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Obvious implementation</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Fake it"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Fake it</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Avoid \"cute\" names"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Avoid "cute" names</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Dependencies"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Dependencies</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"pubspec.yaml"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">pubspec.yaml</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Imports"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Imports</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Cornell Notes"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Cornell Notes</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"README"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">README</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Beams' commit format"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Beams' commit format</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Mike Cohn"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Mike Cohn</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Using the terminal"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Using the terminal</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"\"Problems\" tab"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">"Problems" tab</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Shift-F6"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Shift-F6</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Alt-Enter"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Alt-Enter</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Mind maps"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Mind maps</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Flutter Cookbook"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Flutter Cookbook</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Syntax"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Syntax</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Exceptions"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Exceptions</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Format on save"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Format on save</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Android SDK"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Android SDK</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Following configuration instructions"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Following configuration instructions</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Macbooks are evil"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Macbooks are evil</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Windows Developer Mode"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Windows Developer Mode</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Visual Studio"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Visual Studio</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Stack Overflow"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Stack Overflow</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"git usernames"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">git usernames</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"git commands"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">git commands</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"pull requests"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">pull requests</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Google Drive"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Google Drive</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Google Docs"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Google Docs</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Achievements"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Achievements</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"PowerPoint"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">PowerPoint</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Preparation"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Preparation</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Presentation"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Presentation</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Alternative grading systems"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Alternative grading systems</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Reflection essays"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Reflection essays</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Leap years"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Leap years</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Foo/foobar"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Foo/foobar</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"FizzBuzz"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">FizzBuzz</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"GradeTool"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">GradeTool</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Printing in production code is bad"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Printing in production code is bad</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Cannot build across async gaps"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Cannot build across async gaps</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Demonstrating understanding in assignments"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Demonstrating understanding in assignments</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Patience"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Patience</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Trust the process"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Trust the process</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Wikipedia"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Wikipedia</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Soup"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Soup</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Self responsibility"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Self responsibility</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Career is not a zero-sum game (win-win situations)"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Career is not a zero-sum game (win-win situations)</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"EMRF"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">EMRF</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Arxiv"}" style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Arxiv</td><td style="border: 1px solid rgb(204, 204, 204); overflow: hidden; padding: 2px 3px; vertical-align: bottom;"></td></tr><tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"Developer jobs"}" style="-webkit-text-stroke-width: 0px; border: 1px solid rgb(204, 204, 204); color: black; font-family: Arial; font-size: 13.3333px; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; overflow: hidden; padding: 2px 3px; text-align: start; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; vertical-align: bottom; white-space: normal; widows: 2; word-spacing: 0px;">Developer jobs</td></tr></tbody></table>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-38487476001648473112023-11-29T12:32:00.002-05:002023-11-29T12:32:25.411-05:00NoGaDeMon<p>It's almost the end of November 2023, and sadly, I don't have a <a href="https://nagademon.org/">National Game Design Month</a> project to share this year. It was a good run, but I needed to take this November to focus on other things. It's been a busy semester, but a good one. I helped run the<a href="https://www.symposiumongames.org/2023-symposium"> Symposium on Games</a> this month, and I'm the chair of the search committee that is looking to hire a new colleague to help with our games curriculum. I am also currently working on a grant from the Indiana Space Grant Consortium to make a game that helps teach middle school youth about paths to college. We demonstrated our work at the Symposium and received positive feedback, and now the team is making plans for the next steps. In fact, I will have a course release next semester to focus on this project, but since I didn't have one this semester, it was like teaching an extra class to try to keep the project running. I also have a group study of game AI that has been interesting, and those students will be presenting their work in next week's department colloquium. </p><p>So, to make a long story short, no NaGaDeMon this year, but plenty of game design (and game design adjacent) work happening. I am currently in the process of setting up Ball State to host Global Game Jam again in January, and that's the next major jam event on my radar.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-81841695392243577692023-09-29T07:42:00.008-04:002023-10-02T11:26:41.403-04:00Automatically publishing Godot 4.1 projects to GitHub.io<p>Some time ago, I wrote <a href="http://paulgestwicki.blogspot.com/2020/08/a-slightly-easier-way-to-get-started.html">a blog post</a> and made <a href="https://www.youtube.com/watch?v=OREQ9X1SdAw">a video</a> about how to automate the publishing of web-based Godot games on GitHub.io. It worked fine in 3.x, but with the release of 4.x, things got complicated <a href="https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html">because of SharedArrayBuffer</a>. I have come across some tips online that claim to solve the problem, but I was unable to get any of them working entirely as advertised. After some effort last night, I've been able to pull all the pieces together into this GitHub workflow configuration:</p><script src="https://gist.github.com/doctor-g/8e59dba4ed5090e0f089b22109eae7d6.js"></script><p>Briefly, this makes use of <a href="https://github.com/abarichello/godot-ci">barichello's godot-ci Docker container</a> to build the web export for the game, then it brings in <a href="https://github.com/gzuidhof/coi-serviceworker">gzuidhof's service worker</a> to provide the secure context necessary for SharedArrayBuffers. Deployment to GitHub pages is conveniently managed via <a href="https://github.com/peaceiris/actions-gh-pages">peaceiris' actions-gh-pages</a>.</p><p>Here is what you need to make this work:</p><p></p><ul style="text-align: left;"><li>The script assumes you have a "project" folder at the root of your repository, and that your Godot project and its corresponding project.godot file are there.</li><li>You need to have configured HTML5 export and called it "Web," which is the default. Set the export to go to the path "build/web/index.html" off of the repository root. That is, within the export settings, the export path will be "../build/web/index.html".</li><li>Make sure your export configuration (export_presets.cfg) is in the repository. That is the default for Godot 4, but you'll find some sample .gitignore configurations that want to hide that file.</li><li>Your repository must be public.</li><li>You have to enable GitHub Pages in your repository. In the corresponding section of the GitHub repository settings, make sure your source for the pages is to "Deploy from a branch" and that the branch you select is "gh-pages."</li></ul><div>I was blindsided by the HTML5 export complications when trying to build a web version of <a href="https://ldjam.com/events/ludum-dare/53/mr-delivery-man">my last Ludum Dare game</a>. I'm hopeful that this new configuration will make my deployment easier for <a href="https://ldjam.com/events/ludum-dare/54/">Ludum Dare 54</a>, which starts tonight.</div><div><br /></div><div>UPDATE: Although I did not complete an LD54 project, I did get far enough to set up GitHub automation. I ran into a problem Saturday that I did not see on Thursday, which was the need for the build system to be able to write to the repository. It comes up in <a href="https://github.com/peaceiris/actions-gh-pages">peaceiris' gh-pages documentation</a>, and the fix is a one-liner: the addition of a permissions statement. I have updated the gist above to include this.</div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-4876444199925362292023-09-25T11:23:00.003-04:002023-09-25T11:23:38.901-04:00Teaching TDD... but what just happened?<p>In my CS222 class, I spend a lot of time talking about, demonstrating, and giving feedback on Test-Driven Development (TDD). Indeed, from the very first day of class, we're doing TDD. For several years, I've opened the course by talking about confidence and how we can take small steps to maintain it. Last week was the fifth week of the semester, and my students had just started the "two-week project." That project integrates the concepts from the first four weeks of the semester into a single deliverable that of course includes TDD.</p><p>On Thursday, I was showing them how we can write a custom parser for data pulled from Wikipedia. I started by writing a test that, given my sample data, my as-of-yet unwritten parser could extract the piece that I want. Then, we wrote a clearly naive implementation of the parser that just returned that value as a literal. The literal has no place in the specification, and so our refactoring moved us to a correct solution. I made a big deal about how we had just completed a red-green-refactor sequence, summarizing each step to them as I have just done for you above. I especially emphasized that at the time of writing the test, we didn't care at all how we would make it work.</p><p>A student pushed back a little on the solution, asking why we didn't just go back and change the test case itself to something else. I responded that this would not be right, because before we wrote any production code, we first had agreed that this case was correct--and it was. I reminded them again about red-green-refactor. At this, a different student raised his hand to comment. These are not his exact words, but his observation was essentially this: "Are you saying, then, that at the red step, we write a test that tells us what we need next, but we don't worry about how it will work; then we make it work however we can; and then we clean it up to make it work right?"</p><p>I paused for a moment and then agreed, telling him that I really couldn't have said it better myself, thinking to myself as I said this that I had, indeed, said basically that same thing, many times in the past five weeks. At this, several heads in the class nodded sagely, making expressions as if they had gained some new insight into the process.</p><p>On one hand, I am happy that they seem to understand it now better than they did before, that this students' summarizing the process seemed to resonate. Yet I am plagued by the question, <i>What just happened?</i> What was actually different about this explanation than before?</p><p>I will see the students again tomorrow, and I'm thinking about just asking them. I don't want to make anyone feel badly about not understanding it before, but I do want to know if they can identify what tipped them toward understanding it now.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-76845840698235894682023-09-20T19:08:00.000-04:002023-09-20T19:08:00.413-04:00The one question to ask playtesters (according to Mike Selinker)<p>I listened to some of <a href="https://podcasts.google.com/feed/aHR0cHM6Ly93d3cudGhpbmtsaWtlYWdhbWVkZXNpZ25lci5jb20vcG9kY2FzdD9mb3JtYXQ9cnNz/episode/NWI0YmMwODU5NmQ0NTVjM2VlYjI4ZjQ4OjViYzkwMzIzNzFjMTBiZTNkYTBlMzhhYzo2M2ZkNzU3YzU1ODdlNTVmMzczNzE2MmQ?sa=X&ved=0CAIQx8UHahcKEwiwk4bppbqBAxUAAAAAHQAAAAAQLA">Justin Gary's interview with Matt Fantastic on the "Think Like a Game Designer" podcast</a> on my walk home from work today. The two were talking about how to interpret playtesting results, and Mr. Fantastic shared some advice he got from <a href="https://www.selinker.com/">Mike Selinker</a>. According to Fantastic, Selinker advises asking playtesters just one question, "What did you do?" </p><p>This question completely avoids problems of the designer asking leading questions. Instead, the designer gets to hear what the player experienced in their journey through the game. It's a lot simpler than some of the other formats I have used and encouraged my students to use, and it's certainly simpler than the formal playtesting process that my students are pulling from Lemarchand. It would be fascinating to run parallel tests with these different formats and see if there's something objective that can be learned from this, but who has time for yet another research project?</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-91552517036863583432023-09-18T14:48:00.004-04:002023-09-18T14:48:41.326-04:00Brief summary of Zinsser's "On Writing Well"<p>I recently read through most of the 4th edition of William Zinsser's <i>On Writing Well</i>, published in 1990. I pushed it to the top of my reading list after reading <a href="https://filiph.net/text/the-engineering-principles-behind-giant-robot-game.html">Filip Hráček's article, "The engineering principles behind GIANT ROBOT GAME,"</a> which references <a href="https://pboyd.io/posts/cargo-cult-of-good-code/">Paul Boyd's, "The Cargo Cult of Good Code,"</a> which lists Zinsser's book as his second favorite book on software design. I enjoyed roughly the first half of Zinsser's book, I worked through another third or so, and then I figured I had gotten what I could from it. I agree with some of the criticism of his book that he uses a lot of words to tell you that you should not use so many words.</p><p>Near the end of my reading, though, he had a helpful recap of the fundamentals that he professes. These are, briefly:</p><p></p><ul style="text-align: left;"><li>Clarity</li><li>Simplicity</li><li>Economy</li><li>Humanity</li><li>Active Verbs</li><li>Avoid windy concept nouns</li></ul><div>The last one merits explanation since "concept noun" is not a term one comes across regularly. An example from his book of a violation of the principle is the sentence, "The common reaction is incredulous laughter," which has no <i>people</i> in it. Compare that to, "Most people just laugh with disbelief," which is something that the reader can visualize. When I think about <i>concept nouns</i>, though, I tend to remember the wrong thing: Zinsser's caution against "creeping nounism" as one finds in the gem, "Communication facilitation skills development intervention."</div><div><br /></div><div>Taken as a set, Zinsser's summarized points provide solid advice, though I am not sure it stands up against <a href="https://www.orwellfoundation.com/the-orwell-foundation/orwell/essays-and-other-works/politics-and-the-english-language/">George Orwell's rules</a>.</div><div><br /></div><div>While I enjoyed most of the book, one part that irked me was how <i>journalistic</i> he treated the wide tent of "nonfiction writing." He argues for paragraphs to be constructed of three, two, or even one sentence. This strikes me as counter to the epistemic value of writing as embodied in the <i>essay</i>—the attempt to understand. That is the space where I do the most serious writing, and it is the space I want my undergraduates to inhabit. For a book-length treatment of writing advice, then, I think I'll stick with <a href="https://www.gutenberg.org/ebooks/37134">Strunk</a>. </div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-37829445687136269512023-09-11T18:54:00.000-04:002023-09-11T18:54:00.980-04:00Notes on Boredom<p>Last Friday, I was able to attend a talk by Kevin Hood Gary about his book, <i><a href="https://www.cambridge.org/core/books/why-boredom-matters/00BE81D1A9A5C5F344AFAEA099AE1F98">Why Boredom Matters</a></i>. The talk was sponsored by the <a href="https://alcuinstudycenter.org/">Alcuin Study Center</a> in Muncie. My wife was interested but unable to attend the presentation, so I took some notes to share with her. A colleague also told me they were unable to attend, so I decided I'd turn my notes into this post, which I can then share with anyone who is interested. Keep in mind that these are extrapolations of my notes from the presentation and should not be taken as a summary of the book. He acknowledged that the talk was designed for a general audience while the book goes into more technical detail. I'm sure any inaccuracies or misrepresentations in this post are my own.</p><p>The thesis of the book—as explained by Dr. Hood—is that we avoid boredom and thereby miss out on leisure. There are different ways to react to boredom, but they fall on a spectrum from <i>avoidance</i> to <i>resignation</i>. We can avoid boredom through amusements. I appreciate that, from early in the presentation, Dr. Hood clearly and explicitly distinguished between amusement and leisure, the former being a distraction and the latter being life-giving. This is a classical distinction that is clear to me, although from the discussion, I think that he has sometimes received pushback on these terms.</p><p>After acknowledging that the smart phone is a boredom avoidance device, Dr. Hood mentioned that he offers extra credit to his students if they put their phones in a box during class meetings. He said they happily take him up on this. I am not a fan of "extra credit" since it leads to inflation, and I wonder what differences would occur in making this simply for <i>credit</i> instead of <i>extra credit</i>. </p><p>Dr. Hood referenced <a href="https://www.science.org/content/article/people-would-rather-be-electrically-shocked-left-alone-their-thoughts">Wilson's shock test experiment</a>. I think I had heard of this before, but I did not have it ready in memory. It's an amazing study from about ten years ago in which people were put in an empty room to be alone with their thoughts... except they could also opt to give themselves a mild electrical shock. Interested readers should learn about the actual experiment, and although I hesitate to spoil some results here, I will, in the name of having these data later for my own purposes. The study showed that 25% of women and 67% of men would rather give themselves painful shocks than be alone with their thoughts. I echo the response of one of the audience members who wondered how this broke down by age. Dr. Hood drew a comparison between this study and Pascal's quotation, that all human evil comes from the same cause: our inability to sit still in a room.</p><p>He also brought up a nice quotation from T. S. Eliot that I don't remembering hearing before. It comes from "Four Quarters" and states that we are "distracted from distraction by distraction." An unsurprisingly poetic interpretation of the theme from Mr. Eliot.</p><p>Dr. Hood briefly mentioned how Kierkegaard described human agency as being balanced between the despair of possibility and the despair of necessity. The former describes, for example, infatuation with celebrity, as well as the hesitation one experiences at trying something at which one might fail. During the Q&A, Dr. Hood confirmed that my understanding of this end of the spectrum was correct when I likened it to meeting incoming students who want to become game developers but who then do not do any of the actual hard work required to succeed in the field: imagining oneself as an ideal is easy, but reifying that ideal takes real effort.</p><p>Rather than eliminating boredom with amusements, we should instead pursue <i>focal practices</i>. These draw upon the classical notions of leisure, and these were connected to historical understanding of <i>scholé</i>. He illustrated the concept by referencing <i>Groundhog Day</i>, which everyone in the audience had seen. (Both he and I were surprised at this, given the range of ages present.) In the movie, Bill Murray's character eventually replaces his amusements and his despair with focal practices such as good conversations, walking, making music, and reading a good book. Dr. Hood talked a little about these as being done for their own sake and being driven by intrinsic motivation. </p><p>Crucial to understanding focal practices is that they always involve a moral threshold. That is, focal practices are preferable to amusements, but they involve making a conscious decision. His example was that he could go home after work and watch sports highlights or he could go for a walk with his wife. The latter is clearly the more life-giving of the two, but it requires a decision to be made. This and several other parts of the presentation got me thinking about the virtues, both as habits of being and as choosing a medium between extremes.</p><p>He contrasted focal practices in an interesting way against videogames, describing how some will claim that videogames are their <i>leisure</i>. He explained that he himself had played a lot of videogames and that he knows that they work by producing a steady stream of delicious, delicious dopamine. He was a little reductive here but not inaccurate. There are hooks here for a scholar to explore the amusement vs. leisure elements of play. (Coincidentally, I was recently in conversation with a colleague about how it's almost certainly better to make videogames as a leisure activity rather than as a job. A "regular" job in software development will pay the bills more reliably.)</p><p>It may be worth noting that throughout the presentation, Dr. Hood never insulted amusement and acknowledged that they play a role in a healthy life. It is the absence of leisure that is the serious detriment.</p><p>Toward the end of the presentation, he offered some suggestions on what we can do about boredom. The first of these is a "boredom audit," in which one tracks how much time is spent avoiding boredom. This raises awareness. I think that sounds like a great challenge and perhaps even <a href="http://paulgestwicki.blogspot.com/2023/07/summer-course-planning-cs222-advanced.html">an achievement for my CS222 class</a>. The second of his suggestions was to identify two focal practices that you enjoy and one you would like to enjoy. Again, this seems to me to be about foregrounding thoughts that one might be avoiding. The third was to pursue friendships of excellence, drawing on Aristotle's understanding of the different kinds of friendship. Here, he pulled in one of my favorite quotations from G. K. Chesterton, that "if a thing is worth doing, it is worth doing badly." (The line has been used and misused, but The Society of G. K. Chesterton has <a href="https://www.chesterton.org/a-thing-worth-doing/">a helpful article</a> explaining its context and interpretation.)</p><p>I enjoyed the presentation, and I look forward to reading the book. The Tower of Unread Books has spawned two or three additional piles throughout my home and office, so it may be a while before I get to it.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-61161443696842301902023-07-21T10:09:00.001-04:002023-07-21T10:09:17.703-04:00Summer Course Planning: CS222 Advanced Programming<p>Regular readers may have sense that I was running out of steam when I wrote <a href="http://paulgestwicki.blogspot.com/2023/05/reflecting-on-cs222-spring-2023-and.html">my reflection of Spring's CS222 class</a>. It wasn't a bad class, but the frustrations around the lack of participation drained my will. It was good to step away from it for a few months. This week, I continued the course planning work <a href="http://paulgestwicki.blogspot.com/2023/07/summer-course-planning-cs414-game.html">that I started last week</a>, and my goal was to determine what I should do with CS222. The results are posted in <a href="https://www.cs.bsu.edu/~pvgestwicki/courses/cs222Fa23">the public course plan</a> (CC-BY, as usual).</p><p>My reflection on Spring's class ends with some ideas about completely overhauling the class. It did not take long for me to realize I would not have time for that, especially not while teaching three classes and leading two independent research groups in the Fall. I did make one major change to the grading policy: I changed the final grades to be determined by specifications rather than weighted average. The major difference is that completing some achievements is now necessary for a C or better grade whereas they used to be a way to separate high performance from standard performance. I took this trick from David Largent, who regularly teaches this course as well. Many of the "achievements" are in fact remedial for students who have bad study habits, and so I think it will be beneficial to be able to push students toward ways that they can use this system to improve those skills. For example, if someone does badly on an assignment from the reading, I can easily point them to the "Re-reader" achievement (also one of Largent's inventions). </p><p>The other major change that I am trying is using EMRF grading rather than triage grading of assignments. Triage grading is elegant although many students refuse to engage with it. (I blame the brainwashing of the rest of their educational experience, particularly the abuse of tools like Canvas and the administrative state's succumbing to the <a href="https://www.amazon.com/Tyranny-Metrics-Jerry-Z-Muller/dp/0691191913/">tyranny of metrics</a>.) For years, students in CS222 have been free to resubmit any assignment from the class, whether they got a middling (2/3), low (1/3), or no (0/3) marks on it. However, unless my perceptions are wrong, I have seen a drop-off in the number of students taking advantage of this. To be clear, I don't know exactly why this is, and there are many competing as well as overlapping hypotheses. Suffice it to say, I don't have much to lose by trying something different. </p><p>I came across EMRF grading from <a href="https://rtalbert.org/specs-grading-emrf/">Robert Talbert's blog</a>. What strikes me about it is that it's not that different philosophically from triage grading. However, because numbers are removed and replaced with letters, students <i>cannot</i> turn to a quantiative understanding. Whereas a student might look at "2/3 points" and think of it as "66% That's failing!" rather than "This is halfway good and halfway poor," I don't think I will see the same thing with someone who gets an "M" despite its serving a similar purpose. It's not exactly the same purpose, and I wonder if I will miss triage grading's ability to call something "kind of right, kind of wrong," whereas the output of EMRF grading essentially Boolean: either you pass or you don't. The choice of "F" for "fragmentary" is the one place where I am sure students will think of it as "failure," and that's an unfortunate impact here: it continues to put a negative rather than a hopeful connotation on the necessary step of <i>getting things wrong</i> on the way to <i>getting things right</i>. I suppose I have time before Fall to replace my link to EMRF grading with the invention of a variation such as EMRX. </p><p>The EMRF system works well with the change to specifications-based final grade. I took another page from my colleague Dave when I characterized the requirements for A, B, C, etc. as having all the assignments passed, all save one, all save two, etc. I have not yet decided whether I will use EMRF grading, triage grading, or something else when it comes to the projects. Right now, the final course grade is based on the letter-grade achievement in the final project, and so I can punt on the decision on how exactly that will be graded; that will give me time to see how the first few weeks with these other changes works.</p><p>One other structural change to the course is worth mentioning. For years, I've used a structure where we have a three week ramp up to the two-week project, then a short break, then nine weeks in the final project, which is completed in three three-week iterations. When I switched to Dart and Flutter, I had to remove some interesting content from those three weeks so that the students could have time to learn more about the technical details of these. I missed some of the elements I pulled out, though, and I desired to add them back in. As another experiment, I annotated each assignment with an estimate of how long I expect it to take, and this exercise actually helped me see that I needed more time for this introductory portion of the course. There was just no way to get all the things in there that I thought were important to practice before getting into bigger projects. Hence, I added an extra week at the beginning of the semester and shrunk down the duration of the iterations of the final project. I think this will be a positive change: it will give students more time to practice some fundamentals such as TDD and Dart programming, and I think shorter iterations of the final project will reduce sandbagging. </p><p>Last semester, an administrator forwarded <a href="https://sites.stlawu.edu/cita/2022/12/31/guest-post-alanna-gillis-an-evidence-based-way-to-improve-students-participation-and-how-to-grade-it/">this blog post by Alanna Gillis</a> to all faculty in my college. To summarize, she frames participation as a skill to be formatively developed rather than summatively graded, managing this with student participation self-reflections at the beginning, middle, and end of the semester. I think this is a great idea, and I tinkered with different ways of incorporating it into this class. In the end, though, I ran into a problem that always strikes me when I think about this course: there's already enough (or too much) going on here. There is a definite overlap between the course's four essential questions and Gillis' participation grades, but I fear that students may not have the wisdom and experience to see this. Similarly, many of the achievements could help students directly make progress in these forms of participation, but I already have students reflect on this regularly, such as at the end of each iteration of the final project and the final exam. I <i>do</i> want the students to think about how their participation in the class shapes their learning experience, and I already do that through these reflections. I fear that I would get less coherent reflection by adding more requirements and nomenclature. In the end, then, I decided to work some of Gillis' ideas into my discourse around participation and to use this to encourage students' engagement with achievements (which, again, is now more "required" than it was before), but not to go so far as to bring in new surveys and additional self-assessments.</p><p>I am eager to see how this semester goes. The course is back to Tuesday/Thursday, which I think is much better for the students, but I had to rearrange all my plans to deal with it. Also, it will be directly after my game programming class, so I am little worried about my own ability to maintain focus. It may be a two-coffee semester.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-91597930313855928752023-07-17T09:12:00.001-04:002023-07-17T09:12:15.327-04:00Summer Course Planning: CS414 Game Studio 1<p>Last semester, I taught my department's new game preproduction class for the first time. I wrote a few blog posts about it [<a href="http://paulgestwicki.blogspot.com/2023/02/notes-from-inaugural-game-preproduction.html">1</a>,<a href="http://paulgestwicki.blogspot.com/2023/02/notes-from-inaugural-game-preproduction_18.html">2</a>,<a href="http://paulgestwicki.blogspot.com/2023/02/notes-from-inaugural-game-preproduction_28.html">3</a>,<a href="http://paulgestwicki.blogspot.com/2023/04/notes-from-inaugural-game-preproduction.html">4</a>,<a href="http://paulgestwicki.blogspot.com/2023/04/notes-from-inaugural-game-preproduction_12.html">5</a>], but it seems I didn't write a summarizing essay. It was a great experience, and I have been tasked with teaching our new games capstone sequence in the coming academic year. It is a two-course Game Studio sequence. In the future, it will be interdisciplinary, but because my department is one catalog ahead of our collaborators, our vanguard is entirely Computer Science majors.</p><p>I was glum after having killed my summer side project about a week and a half ago, and so it was originally with some reluctance that I moved into doing some university work. Once I got into it, though, I quite enjoyed pulling my plans together for the first course of the game studio sequence, CS414. I do enjoy this work, but it was also good to have some time away from it.</p><p>I posted my course plan <a href="https://github.com/doctor-g/cs414">as a GitHub repositor</a>y rather than following my custom the last several years of making a web site for the course. I was inspired in part by Robert Talbert's repositories (<a href="https://github.com/RobertTalbert/discretecs">example</a>), which I came across after reading through some of his alternative grading posts. <a href="https://rtalbert.org/specs-grading-emrf/">His presentation of EMRF grading</a> stuck in my head for potential use, although I decided against using in CS414. It was definitely faster to type up a course plan as a directory of Markdown files, but the result is unarguably less elegant than my usual course sites. Consider <a href="https://www.cs.bsu.edu/~pvgestwicki/courses/cs315Fa23/overview">my CS315 Game Programming site</a>, where Javascript functions remove the redunancy necessary for allowing both presentation and download of checklists, or <a href="https://www.cs.bsu.edu/~pvgestwicki/courses/cs222Sp23/overview">CS222</a>, where custom components and stylesheets make achievements look enticing. In my mind, the jury is still out, and I'm not sure which approach I will take for the third course I need to prep this summer. (Also, an unrelated discussion on the SIGCSE mailing list brought me to <a href="https://sfconservancy.org/blog/2022/jun/30/give-up-github-launch/">a coherent argument to stop using GitHub</a>, and now that's stuck in the back of my head as well.)</p><p>As part of my preparatory work, I read the second half of Lemarchand's text that we had used for the preproduction class. I decided that we will continue to follow his process, taking the conventional production approach of dividing the rest of our time into an alpha phase, a beta phase, and postproduction. I'm following his advice on the proportion of time to each of these activities, and this has the alpha phase wrapping up about a week before the end of the Fall semester, and the beta phase will finish two weeks before Spring Break in the subsequent course. I contemplated applying instead the Scrum-based approach to planning that I have used in my one-semester immersive game studio classes, but this is a good opportunity for us to follow Lemarchand's advice from the trenches and also for me to learn something new.</p><p>I spent a lot of time thinking and tinkering around the best process and tools to recommend to my students. There were two key concepts that kept coming to mind. The first is that I want to gain the benefit of how I use two-week sprints in my usual game studio courses. We do this by planning out a two-week increment and estimating the time required for all the relevant tasks. I manage the backlog grooming, story articulation, and prioritization; the students determine which stories to address, the tasks required, and the estimated hours for each task. The students update their hours after every work session, and this lets me draw a burndown chart to map steady progress against actual progress. At the end of each sprint, we talk about how it went, using a retrospective format to update the methodology. This approach gives students ownership over the execution of the project, forces them to communicate with each other, gets them into a mode of reflective practice, and makes them think about estimation. It has the disadvantage of not actually being agile, as many methodologists have pointed out: committing to stories for a sprint is the polar opposite of embracing change. This has the corresponding issue that students (and sometimes I!) confuse estimate and commitment; this sometimes manifests as students becoming martyrs for the project, but more often it manifests as students not meeting their goals because they haven't managed their time prudently.</p><p>The second concept that I've wrestled with is that I want to suggest to them an appropriate level of tool support that will help them succeed without getting in their way. This led to a lot of research and consideration. This is not an exhaustive list, but I want to capture a few notes here so that I can find them later.</p><p></p><ul style="text-align: left;"><li>Taiga: Disadvantage: In Scrum mode, forces the use of user stories and story points for tracking work. I do not want to use story points, and I would like to be able to track tasks outside of stories (which is something <a href="https://www.scrum.org/resources/scrum-guide">Scrum</a> permits).</li><li>HacknPlan: Disadvantage: Enabling hour estimates also enables hour logging, which is something I don't want to use. It has no built-in way to track changing estimates over time: changing the estimate of a task or story is a lossy operation.</li><li>GitHub Projects: Everything is an "issue," which is not ontologically right, and it has no way to articulate something like a story as comprising of tasks. (There seems to be <a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists">such a feature</a> in beta for public repositories, and I think that's being used in <a href="https://github.com/orgs/godotengine/projects/58">Godot's project trackers</a>, but it looks like I do not have access to it.)</li><li>Lemarchand's Google Sheets Templates: Require manual copying of data across sheets, producing redundancies that any good project management system should eliminate.</li><li>Yoda: I could not make this work for private repositories.</li></ul><div>In the end, I chose HacknPlan. I made a video a few years ago about <a href="https://youtu.be/hfbcO7fxa2g">how to incorporate a manually-updated burndown chart into HacknPlan</a>. Unfortunately, there was a part of the process that I forgot to put into the video: where do the data come from? Since HacknPlan's estimate changes are lossy, one needs to get the daily data from HacknPlan into the spreadsheet. The way this is done is by using the information ("i") button on a HacknPlan board, which will show you the current number of work items and hours remaining. </div><p></p><p>Lemarchand's presentation of the production process leaves a gaping hole in moving from putting together a schedule to executing the work. He points out that if you have more than a few weeks of work, it should be broken down into smaller chunks; I am sure he is thinking of something like Scrum sprints here. What, exactly, is the relationship between the original schedule, the macro document, and sprints, though? </p><p>Here's a small example to illustrate how this becomes complicated. For preproduction, one of my teams articulated the task to model a spaceship; it's a top priority task and is estimated to take four hours. For the purposes of the alpha milestone, however, it doesn't have to be completely finished: it only has to be representative, enough to allow feedback to be collected and taken into account before polishing. What, then, happens to that original work item on the schedule? A four-hour, high-priority modeling task that has to be done before the end of the <i>project</i> is actually at least two different modeling tasks, with two different priorities, potentially happening at different times or even by different people. One way to manage this is to decompose the task into two that sit in different user stories, each with different conditions of satisfaction: the first could specify that the model be "good enough for alpha," for example. </p><p>Coincidentally, I recently re-watched <a href="https://www.youtube.com/watch?v=QVBlnCTu9Ms">Allen Holub's 2015 NoEstimates talk</a>. It's worth watching, and one of the most important points he makes is that <i>projections</i> are more valuable than <i>estimates</i>. He also advocates (elsewhere, I think) eliminating sprints in favor of simply doing the most important thing next. This inspired me to try something new: abandon Scrum-style sprints in favor of a more agile alpha phase. The students will be responsible for slicing the problem into stories and tasks, and we will use their progress early in production to project whether or not they will meet their goal. I created <a href="https://docs.google.com/spreadsheets/d/1rWAuDTC1M8Hqz2veOhbgaU07WsLY_ngdit2XNtbg-XU/edit?usp=sharing">a spreadsheet template to facilitate this</a>, using data about the number of work items remaining and the number of hours remaining to forecast when the work will be complete. Here are two charts that it outputs based on three weeks of completely fabricated data.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnj8YRJYHD8Enzyc608OpB6h1Ujt87T2pJ3KzVUOYlmfGgUodtnDIki-Hcrf1mnz55UZNPZjz4CkGxepG763H65CghI5qr9b1jj4zz3SnR3yk3JN1EaGubMFPKuFm-_uDFAEI-30YUgqHSKE32rVpvzKiKXC9KtUU42XR--EmQ9zraZaN6JPRiwrbTIQ/s600/Work%20Items%20Projection.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="371" data-original-width="600" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnj8YRJYHD8Enzyc608OpB6h1Ujt87T2pJ3KzVUOYlmfGgUodtnDIki-Hcrf1mnz55UZNPZjz4CkGxepG763H65CghI5qr9b1jj4zz3SnR3yk3JN1EaGubMFPKuFm-_uDFAEI-30YUgqHSKE32rVpvzKiKXC9KtUU42XR--EmQ9zraZaN6JPRiwrbTIQ/s320/Work%20Items%20Projection.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8eIgVzeRWItHkgGbLNspy0s734Ps-DNk9U3TKR8P9vft5K3Gs-mT24vKujV3oMbO3002REcCbUpGYI0Nr0BZpsMJPBxp6zvQi3Nk3mo0TlnEip2Qks-ayqAciea5kLKoZjJfSOUQrY5AX16wSYsvLvB5IcYY64OIAf9RXCIucrf5Fxs5_qGUBtHUMDQ/s600/Hours%20Projection.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="371" data-original-width="600" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8eIgVzeRWItHkgGbLNspy0s734Ps-DNk9U3TKR8P9vft5K3Gs-mT24vKujV3oMbO3002REcCbUpGYI0Nr0BZpsMJPBxp6zvQi3Nk3mo0TlnEip2Qks-ayqAciea5kLKoZjJfSOUQrY5AX16wSYsvLvB5IcYY64OIAf9RXCIucrf5Fxs5_qGUBtHUMDQ/s320/Hours%20Projection.png" width="320" /></a></div>I have scheduled biweekly studio meetings where the teams will be required to interpret these projections and determine what it means for their project goals. I am eager to see how this compares to two-week Scrum sprints, especially since several of the students have already experienced that approach to game project management. Incidentally, I am happy to receive feedback on my use of the forecast function in the spreadsheet. I've never used it before, and I'm not a statistician.<br /><p>The final piece of the course design puzzle is grading. My first draft of the course involved some intentional scaffolding and deadline around things like telemetry and playtesting. As I began to work more toward an agile, student-driven approach, I felt a corresponding desire to remove such requirements and replace them with mentoring. That is, rather than demand <i>a priori</i> that teams have game metrics set up by week number four, I will have them read and discuss the book and then consider for themselves when these need to be in place. That may sound like inviting foolishness, but Lemarchand provides some very good, clear checklists around things like metrics and testing. Whereas the portion about managing work is light, these other sections are extremely detailed. Indeed, it's clear from reading the book that perhaps the most important, most stressful role on the project will be formal testing coordinator.</p><p>This resulted in a minimalist grading system. Keep in mind that this is a small class, and I have had all of these students in at least one class prior; most I have had in two or three other courses. The alpha milestone is satisfactory if it meets the following criteria, the first of which is a meta-criterion.</p><p></p><ul style="text-align: left;"><li>The project meets the Alpha Milestone criteria described in Lemarchand Chapter 28.</li><li>At least one formal playtest was conducted, with the results analyzed and presented in a studio meeting, during the alpha phase.</li><li>Game metrics are incorporated as per the checklist in Lemarchand Chapter 26 (page 275). The integration, analysis, visualization, and use of feedback to refine the game design have been presented to the studio during the alpha phase.</li><li>An appropriate bug tracking process is established, documented, and put in use.</li><li>Coding standards are established, documented, and followed.</li><li>The milestone is presented at the Alpha Milestone Review.</li></ul><div>That is most of the semester's work. For individual student grades, I took a specs-based approach:</div><blockquote><div>Meeting these criteria earns a D or better grade.</div><ul style="text-align: left;"><li>Your alpha milestone does not satisfy its requirements before the final exam period begins.</li><li>You complete one of your one-on-one meetings with the professor.</li></ul>Meeting all the previous criteria as well as the following earns a C or better grade:<div><ul style="text-align: left;"><li>Your team's alpha milestone satisfies its requirements before the final exam period begins.</li><li>You have completed both of your one-on-one meetings with the professor.</li></ul>Meeting all of the previous criteria as well as the following earns a B or better grade:</div><div><ul style="text-align: left;"><li>Your team's alpha milestone satisfies its requirements by the published deadline.</li><li>You have successfully started your beta production phase before the start of finals week.</li><li>You have resolved all issues arising from your one-on-one meeting with the professor.</li></ul>Meeting all of the previous criteria as well as one of the following earns an A grade:</div><div><ul style="text-align: left;"><li>You have served as a playtest coordinator and ensured that all of the requirements and goals of the playtest are met.</li><li>Another team member gives you a commendation for excellence via email to the professor. Each student may give only one such commendation.</li></ul></div></blockquote>
<p>There are few enough students that one-on-one meetings will not be onerous for me, and it will give an opportunity for honest conversation outside of the studio and out of teammates' hearing. The teams should be able to hit the milestone as long as they are paying attention, since they themselves control the scope of their project. I thought about using something like achievements to reward someone for being a playtest coordinator, but I ended up just slotting it into one of two A options. I haven't used a commendation system before, but with such a small group, and where all the work is really transparent to the whole studio, I want to give this a try. I fully expect pairs of students to agree to give each other commendations, and that's fine; they still cannot satisfy the alpha milestone requirements unless more than one student picks up the playtest coordinator role.</p><p>That's a good summary of my work last week in preparing for this course. I am not exactly in a rush for the semester to start, but if it started tomorrow, I'd be happy to start working with these students.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-61801738028384503112023-05-21T07:07:00.005-04:002023-05-21T07:08:42.874-04:00Noodles<p>There are many reasons to come to this blog, but the most important one is probably the recipes. Here is where I share with you important information, such as <a href="http://paulgestwicki.blogspot.com/2022/09/making-truffles.html">the ratio of chocolate to cream when making truffles</a> and <a href="http://paulgestwicki.blogspot.com/2023/02/amari-4-and-5.html">how many arbitrary things I've thrown into alcohol to make drinks</a>. Crucially, this is a place where I can come and find things that I might otherwise forget. Since it is an Internet Law that one must share a story before a recipe, I will give you an example.</p><p>I have fond memories of homemade chicken soup. When I was sick, my Polish grandmother would make chicken soup with big, soft noodles. I asked her how to make these noodles, and her answer was basically that they are just noodles—you just add flour and eggs and water until it's right. That is undoubtedly the proper way to make them, but it led to many years of my unsuccessful attempts to recreate something like her noodles. For some time, I was making something more like pasta, which I'd roll out and cut. These were made using a cup of flour, and egg, a pinch of parsley, and just enough water to hold it together. This was based on a combination of my grandmother's advice and online recipes for kluski.</p><p>A recent issue of <a href="https://www.177milkstreet.com/magazine">Milk Street magazine</a> featured a recipe for Hungarian dumpling noodles called "nokedli." After reviewing the issue, I had remembered the name incorrectly, which explains why my arbitrary combination of letters was returning no results. The article claims they are like German spaetzle, which puts it certainly in the same category as a comforting Polish noodle. I put these into my next soup, and I'm sure these are much more like what my grandmother meant with her advice. What I mix up now is batter rather than dough, and I roughly flick it into the boiling water off the back of a cutting board rather than carefully cut and separate them before dropping them in.</p><p>Look at that, only three paragraphs so far and <i>not a single ad</i>. You're welcome. Here's the ratio that makes it all happen:</p><p></p><ul style="text-align: left;"><li>2 cups of flour</li><li>2 eggs</li><li>1 cup of water</li></ul><div>Mix it up, cover it, let stand for half an hour or so, and that's really all there is to it. The magazine's recipe also calls for 1 tsp. salt, 1/2 tsp. pepper, and 1/2 tsp nutmeg. These are fine things to add, though last night I tried making noodles without these, and I do not think anyone noticed. Since I'm throwing them into soup, they pick up the flavor of the broth. If they were being served as a standalone carb, they would probably want something like spices or butter.</div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-53124343282113653612023-05-13T08:47:00.001-04:002023-05-13T08:47:19.238-04:00Awaiting multiple signals in Godot GDScript<p>I ran into a problem with my summertime project where I want to wait for multiple coroutines or signals to complete. These happen to be related to animations. Depending on the situation, multiple different animations may play together, but I don't want to advance until all of them are complete. I have used some other asynchronous programming libraries that have built-in support for this, but alas, Godot has no such abstraction. </p><p>I built this utility class that fills the bill. As with <a href="http://paulgestwicki.blogspot.com/2023/05/running-godot-unit-tests-using-gut-with.html">my previous post</a>, I decided to share the code here since the "live" version is locked away in a private repository.</p><pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); color: black; font-family: "Andale Mono", "Lucida Console", Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class_name CompositeSignal
extends Node
signal finished
var _remaining : int
func add_signal(sig: Signal):
_remaining += 1
await sig
_remaining -= 1
if _remaining == 0:
finished.emit()
func add_call(object, method_name:String, argv:Array=[])->void:
_remaining += 1
await object.callv(method_name, argv)
_remaining -= 1
if _remaining == 0:
finished.emit()
</code></pre><p>This was developed using TDD using <a href="https://github.com/bitwes/Gut">GUT</a>. Here's the test code. There's not actually a test here to demonstrate that the two add_ techniques work together, but that is exactly how my integration works in practice.</p><pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); color: black; font-family: "Andale Mono", "Lucida Console", Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>extends GutTest
## The amount of time a timer can wait that is safe for a unit test to run
const _TIMER_DURATION := 0.1
var _executions := 0
func before_each()->void:
_executions = 0
func test_add_call(p=use_parameters([[1],[3]])):
var composite = autofree(CompositeSignal.new())
var number_to_add :int = p[0]
for i in number_to_add:
composite.add_call(self, "_run_coroutine")
await wait_for_signal(composite.finished, 2, "Signal should have returned")
assert_eq(_executions, number_to_add, \
"There should have been %d invocations." % number_to_add)
func _run_coroutine()->void:
await get_tree().create_timer(_TIMER_DURATION).timeout
_executions+=1
func test_add_signal():
var generator :SignalGenerator = autofree(SignalGenerator.new(self))
var composite = autofree(CompositeSignal.new())
composite.add_signal(generator.a_signal)
generator.run()
await wait_for_signal(composite.finished, 2, "Signal should have fired by now.")
assert_true(generator.completed, "Expected to wait for generator state to update")
class SignalGenerator:
signal a_signal
var context
var completed := false
func _init(context_p):
context = context_p
func run():
await context.get_tree().create_timer(_TIMER_DURATION).timeout
completed = true
a_signal.emit()
</code></pre>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com2tag:blogger.com,1999:blog-548613718636739636.post-34094066835077682352023-05-10T09:44:00.002-04:002023-05-10T09:46:53.793-04:00Running Godot unit tests using GUT with a pre-commit hook<p>I've been tinkering with a project idea for months, creating and discarding over a dozen variations. I was hoping to have a prototype or vertical slice complete before the start of summer, but I was unable to do so. This current variation is promising, but I ran into a situation where a lot of the code I had laid down was hard to test. In particular, I needed to check the interaction of subsystems that were probabilistic, and that's not only difficult to do manually, it's also prone to regression. Time for unit tests!</p><p>I'm using Godot Engine 4.0, and so I brought in GUT and started redesigning my code to be more testable. This alone is almost certainly worth the effort. Tests lose their value if they are not executed, and so I started looking into automatic ways to run my tests. Godot Engine has no such system built in, but I am using git, and so I investigated pre-commit hooks. This is something I've known about, and I think I may have even tinkered with before, but I don't believe I have ever incorporated them into a project. My previous work with automation used IDEs with built-in capability to run tests on commit, or I used server-side integration tests to check my work.</p><p>Someone who does a lot of shell scripting and git hooks would probably laugh at how simple this is. However, I want to share the script here in part because the "live" version is currently hidden away in a private repository. Here is the executable script that I put into my project's .git/hooks directory:</p>
<pre style="background-color: #eeeeee; border: 1px dashed rgb(153, 153, 153); color: black; font-family: "Andale Mono", "Lucida Console", Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>#!/bin/sh
if godot -d -s --path project addons/gut/gut_cmdln.gd -gdir=res://test/ -gprefix="" -gsuffix="_test.gd" -gexit
then
echo "Tests passed"
else
cat <<\EOF
Unit tests failed. Commit aborted.
EOF
exit 1
fi
</code></pre>
<p>
This works pretty well. One downside is that it has to pop up a window during the tests. I would like to find a way to prevent that. Also, I could probably get away with having the tests run only when I'm committing to master rather than on feature branches.
</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-36286551873274574942023-05-03T09:12:00.007-04:002023-05-05T09:38:59.134-04:00Reflecting on CS222 Spring 2023 and the problem of participation<p>This was my second semester using Dart and Flutter in CS222, and I have no regrets about the platform and language change. I still have many lingering questions about how best to introduce the topics and pace them, and I have some related research questions as well: Does learning null safety make students better or worse at managing null values when using a non-null-safe language? Does Flutter's declarative UI approach make students more or less able to learn other UI frameworks—as well as game engines—later?</p><p>The other major change that was made just this semester was the separation of assigned non-project work into "activities" and "challenges," where the latter can be resubmitted for a higher grade <i>a la</i> mastery learning but the former are fixed in time. From a philosophical point of view, I think this also worked, in that it captured in words something that was previously only in my mind. The presentation and distinction among these can be clarified to the students, and it probably should be, given the small but non-zero number of students who tried to resubmit "activities."</p><p>There were three projects this semester, and all of the teams did a fine job bringing the programs together. Unfortunately, all three also dropped the ball at the end when it came to rigor. Some had made changes to their code that broke their unit tests, while others had no indication of understanding TDD despite having reasonable model-view separation. Put another way, I think they got caught up in cowboy coding at the end, trying to make it <i>work</i> rather than trying to make it <i>right</i>. This is a strong temptation to students, and while it's frustrating, it is also an excellent assessment of whether or not they learned the topics of the class. The central topic of the course is, essentially, that software development <i>should be done right</i>. I don't think there are any traps here besides the ones the students set for themselves, but I will continue to try to find ways to keep the presentation of expectations clear. I still suspect that very few students actually read and review the requirements, preferring to do what they want rather than what I ask.</p><p>A much more troubling observation from the semester is the very low levels of participation in the mastery grading aspect of the class. I haven't kept rigorous notes, but I remember feeling like participation was low last semester. This may be a trick memory, though, given how many other people on campus are talking about low engagement rates. I sat with my spreadsheet this morning to try to quantify participation, and I have included the table below.</p>
<div style="overflow-x: auto;">
<table border="1">
<tbody>
<tr><td>Score</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>3</td><td>10</td><td>10</td><td>4</td><td>4</td><td>3</td><td>7</td><td>3</td><td>5</td></tr>
<tr><td>2</td><td>0</td><td>1</td><td>6</td><td>4</td><td>4</td><td>1</td><td>1</td><td>1</td></tr>
<tr><td>1</td><td>1</td><td>1</td><td>3</td><td>0</td><td>1</td><td>0</td><td>4</td><td>1</td></tr>
<tr><td>0</td><td>3</td><td>2</td><td>1</td><td>6</td><td>6</td><td>6</td><td>6</td><td>7</td></tr>
<tr><td>% Satisfactory</td><td>71.43%</td><td>78.57%</td><td>71.43%</td><td>57.14%</td><td>50.00%</td><td>57.14%</td><td>28.57%</td><td>42.86%</td></tr>
</tbody>
</table>
</div>
<p>The columns after the first are the challenges. Following triage grading, a student can earn 3, 2, 1, or 0 points for each: 3 means it's basically correct, 1 means it is incorrect, 2 means it is middling, and 0 means it was not turned in. </p><p>The table tells an important story of the semester. The first challenges, which were quite easy, were completed successfully by almost everybody. Not all of these were successfully completed on the first try, of course, but they were done. Almost immediately, by week two, there's a significant decline in correctness. This is followed, between weeks two and three, by a significant decline in participation. By the fourth challenge, a plurality of students did not submit anything at all. They also did not take the opportunity to resubmit this work despite many reminders and even admonitions. To be clear, especially at the end of the second iteration of the final project, I pointed out to them the low rates of participation, and I posed the question, "How will you be able to apply principles for chapters you haven't read?"</p><p>It's not a rhetorical question, but no one was able to answer it. I am really baffled. It should be no surprise that "good students" did the reading, submitted things, got some feedback, and then showed some understanding of the concepts. I really don't know what happened with the other ones. Did they forget, time and again? It's possible, since they also don't write anything down. Did they not care? Also possible: maybe they see the class as an impediment to be avoided rather than an adventure to be undertaken. </p><p>Here's the real kicker: 5% of the students' grades comes from participation in the achievement system. The achievements are meant to be fun explorations of new ideas that clearly connect the concepts of the class with students' interests. Many are designed to help them become better learners. This semester, only two students submitted any achievements at all. Those two earned 10 and 12 points out of possible 12. No one else submitted any, nor did they even ask about them, despite repeated reminders. What makes a student look at a part of a class like this and simply reject it? The conventional wisdom for teachers is that if you value something, put points on it, but when 12/14 of the class won't even pursue the points, you know there must be something else going on.</p><p>Without answers to those questions, I am left unsure of what kinds of change might improve participation. I have kicked around some ideas of a major overhaul to the course, using only badges and gated progressions. Given my other goals for the summer, though, I don't think I will have the spare cycles to commit to something like that.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-63740138439937296052023-05-01T21:28:00.001-04:002023-05-01T21:28:09.013-04:00Ludum Dare 53: Mr. Delivery Man<p>Ludum Dare 53 took place the weekend before final exams. This is a great time for a professor to step away and do some creative exploration, but it's not a good time for my students. Truly, it's hard to find a good time for students to jam since it's not like the rest of their classes hold off for a bit so they can spend 48 hours making something, despite the fact that this might be the single best way to get better at making things.</p><p>The theme for Ludum Dare was "Delivery," and at first I was a little disappointed. It felt a little like people were voting on the worst part of RPG quest grinding. After some discussions, I realized there are a few different interpretations, and I look forward to exploring more of the games during the scoring period. For my own game, though, one of my first ideas is what really stuck: a delivery person who manhandles packages. The result is <i><a href="https://ldjam.com/events/ludum-dare/53/mr-delivery-man">Mr. Delivery Man</a>.</i></p><p>I read the theme Friday night but could not set to work until Saturday morning. It did not take me long to get a minimally playable proof-of-concept implemented: drop a package and kick it as far as you can. I sketched some character ideas on paper, and then I pulled out my Wacom Bamboo to draw them up in Gimp. I feel much more comfortable sketching in pencil than I do doodling with my digital tablet. Every time I do something like this for a jam, I feel like I should invest some more time in getting fluent with the tools. Then I think about how long that would take and deprioritize it. Still, I wish I had more confidence. I watch my art majors with their tablets, and they fluidly add layers, sketch ideas, and move on. All my ideation is on paper, which makes the digital production feel weighty, leading to a lot of reworking. It's not like you'd know it from the results, either. They are only good enough.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJZoWR68nMljbD67S3hWNny_ssLXMN2xYS72LIAwljcMOnkXrjn-N8aAmVxwFaI3bTC7LbcGXh1LCpmYJZj_gDfWi44HfxPsZ6q8JTM2kMjhykZzBUH94aB53cZ-SyLzqBNFqJS38YKwYwGDr-5AUrEZnYftAZyJR5sjVxfqtibFPtJHzkCNhFZ6E/s1843/PXL_20230502_011620257.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1724" data-original-width="1843" height="299" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJZoWR68nMljbD67S3hWNny_ssLXMN2xYS72LIAwljcMOnkXrjn-N8aAmVxwFaI3bTC7LbcGXh1LCpmYJZj_gDfWi44HfxPsZ6q8JTM2kMjhykZzBUH94aB53cZ-SyLzqBNFqJS38YKwYwGDr-5AUrEZnYftAZyJR5sjVxfqtibFPtJHzkCNhFZ6E/s320/PXL_20230502_011620257.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A digitally-enhanced photograph of a goofy pencil sketch of some character ideas</td></tr></tbody></table><p>I tinkered with trying to add a knee joint to get something with a QWOP feel, but I ended up preferring the straight-leg kick. I also spent a little time trying to animate the character as if he was holding the package before dropping it, but this proved tiresome and didn't significantly add to the gameplay. The result is that he's got a bit of Homestar Runner magic in how he holds that box in the air.</p><p>Recording the sound effects was some of the most fun I had on the project. I did a little quick research on how to Foley the sound of glass breaking. I took a break from that to make an afternoon coffee. Opening the cupboard, I was inspired to clink some mugs together. Perfect. I recorded myself clinking mugs together for the glass sound, and then I punched a spare board game box for the impact sounds. Conveniently, the box had some bits in it, which gave it a nice depth of sound. It's subtle, but it's quite nice. Whenever the box is struck, one of four random glass sounds and one of four random impact sounds are mixed together, so even though it's a small set of sounds, it still feels audibly satisfying.</p><p>The music also brought be great joy. Tritones play on tremolo strings before dropping the box, and this adds lovely tension to the very silly experience. There is a moment of silence as it falls. I experimented with playing audio through the fall and the kick, but this was much less fun than just bringing in the music after the box came to a rest. I think the polka is just right for a successful kick, and the sad trombones came out as I desired. I had to look into LMMS' controller tracks and pitch variation for the trombones. This feature didn't work quite like I wanted, since I wanted to mix square and interpolated forms; there may be a way to do this, but I couldn't find it. I was able to get a satisfactory sound by adding enough control points to a curved wave.</p><p>By the end of Saturday, I wanted to get a Web build up to make sure everything was working as intended. Here is where the headaches began. My usual continuous integration configuration makes use of <a href="https://github.com/marketplace/actions/godot-ci">the godot-ci action</a>, but it does not currently support Godot 4.0. <a href="https://github.com/abarichello/godot-ci/issues/98">There is an open issue about this</a> that actually had some discussion contemporaneously with Ludum Dare 53. <a href="https://github.com/abarichello/godot-ci/issues/98#issuecomment-1528966678">The latest post</a> points to an alternative approach. I looked into that, and after a lot of frustration and back-and-forth with GitHub Actions (push, wait, check, fix, repeat), I ran into a more troubling issue: <a href="https://docs.godotengine.org/en/4.0/tutorials/export/exporting_for_web.html">Godot 4.0 Web build requires SharedArrayBuffers</a>. I had just encountered SharedArrayBuffers on Friday when working with my game studio team: a subset of them were trying to fix an audio stuttering problem, and we had discovered that we could address it by enabling the "Threads" option on the HTML5 export. This worked fine on our test machine but then failed in integration. This is because SharedArrayBuffers require custom http headers, and GitHub Pages does not allow such a thing. All of this came after I had just added on-screen buttons for all the controls, assuming the Web build would work when deployed, but now I had to consider the real possibility that this would be strictly a native game. The biggest problem with this is that it's much harder to integrate with the Ludum Dare review community if you don't have a Web build. Indeed, <a href="https://ldjam.com/events/ludum-dare/53/rip-ridiculously-injury-prone/filter-games-by-upload-type">one of the threads I just saw on the LD site</a> was asking for a filtering feature since the author did not want to run any non-Web games.</p><p>Somewhere on my journey through many search results, I saw someone mention that itch.io had recently added custom header support. Whether this was specifically to support Godot 4.0 games or a beneficial coincidence, I do not know. A quick test showed that, indeed, this worked just fine. While I prefer just having all my jam projects in one place, and that place being GitHub, it was very easy to get the version on itch.io playable. I set up <a href="https://github.com/doctor-g/LD53/blob/master/Makefile">a Makefile</a> to automate the generation of the Web build and its zipped contents, but I have not yet gone so far as to automate uploading to itch.io <a href="https://itch.io/docs/butler/">using butler</a>. If I have to continue using itch.io as a platform for jam games or side projects, I will have to look into it.</p><p>It was midday on Sunday before I had the deployment issues sorted out, and I still had the energy to do a little bit more with the game. Something that had inspired me early on in the project is that just kicking the boxes around is a fun toy, and I figured I could use achievements to reward players for trying different things. <i>Mr. Delivery Man</i> then became my first game with achievements. I think that they serve the design purpose very well, and this sentiment is echoed by the early comments on the game during the Ludum Dare rating period. The software system behind the achievements it not terribly clever: each achievement is an object with a function that can tell whether the achievement has been met based on a game record, and after each play, the list is checked to see if there's any new ones met. I had hoped that after implementing my first achievement system, I would have a better idea about how I would want to do it next time. The truth is that it was just sort of pieced together, and I'm not sure I gained any great insight into implementation patterns.</p><p>That's the story of <i>Mr. Delivery Man</i>. He was almost named <i>Delivery Dan</i> in an homage to great characters in both Red Meat comics and Firesign Theatre, but leaving him anonymous ended up feeling right. Once again, <a href="https://ldjam.com/events/ludum-dare/53/mr-delivery-man">here is the Ludum Dare page for the project</a>; while you are there, consider checking out <i><a href="https://ldjam.com/events/ludum-dare/53/delivery-destroyer">Delivery Destroyer</a></i> and <i><a href="https://ldjam.com/events/ludum-dare/53/messenger">Messenger</a></i>, which were created by two of my sons.</p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0tag:blogger.com,1999:blog-548613718636739636.post-34432169134558771592023-04-28T15:41:00.002-04:002023-04-28T15:41:52.845-04:00What we learned in CS222: Spring 2023 edition<p>Today was the last day for me to talk to my CS222 class. Monday's class will be their final presentations back to me. As part of my wrap-up, I kept my tradition of having the students make a list of what they learned and then voting on the things that are the most important to them. This year's CS222 class came up with 99 items in 15 minutes. I gave each of then six stickers for voting. The winners are:</p><p></p><ul style="text-align: left;"><li>TDD (6)</li><li>Version Control (6)</li><li>Clean Code (5)</li><li>Red-Green Refactor (5)</li><li>git & GitHub (4)</li></ul><div>One could argue that there are two pairs in that list that are essentially equivalent. There was only one item with four votes and many with three, though, and so this was a convenient place to draw the line.</div><div><br /></div><div>For the deeply curious (or future self who wishes access to these data for research purposes), here's the complete list of items the students came up with and their vote totals. </div><div><ul style="text-align: left;"><li></li><li>TDD (6)</li><li>Version Control (6)</li><li>Mob Programming</li><li>CRC Cards</li><li>Meaningful Iterations</li><li>Model-view separation (2)</li><li>Dart (1)</li><li>Flutter (2)</li><li>Studio</li><li>Clean Code (5)</li><li>Teamwork</li><li>Agile</li><li>SRP (2)</li><li>Prioritization</li><li>Google is your Friend (1)</li><li>Stack Overflow (2)</li><li>Defensive Programming (2)</li><li>OOP (1)</li><li>APIs (1)</li><li>Legibility</li><li>git & GitHub (4)</li><li>UI stuff (1)</li><li>Branch management (1)</li><li>Time management (1)</li><li>Error correction</li><li>Data modeling</li><li>Diagnosing</li><li>Debugging (1)</li><li>Breakpoints (2)</li><li>Refactoring</li><li>Accountability</li><li>Hot keys</li><li>User stories (1)</li><li>SMART</li><li>Product delivery (1)</li><li>Finding packages</li><li>Researching over invention (1)</li><li>Human success & failure modes</li><li>Change notifier</li><li>Enumerated types</li><li>Acceptance testing (3)</li><li>Red Green Refactor (5)</li><li>Conditions of Satisfaction (1)</li><li>Humble view</li><li>Essay formatting</li><li>Experimental code (2)</li><li>Learning tests (3)</li><li>Production code</li><li>When are we done?</li><li>Human interaction (1)</li><li>Generalization</li><li>Class work vs career work</li><li>Design thinking (1)</li><li>Formatting</li><li>Take a break and come back</li><li>Fonts</li><li>Utilizing professor's feedback</li><li>Async</li><li>Work distribution</li><li>Managing state (3)</li><li>Widgets</li><li>DRY (2)</li><li>Scaffolding in UI Design</li><li>Mobile vs Desktop views</li><li>Releases (e.g. EXEs)</li><li>GitHub Actions (2)</li><li>Tagged releases</li><li>Commit messages (1)</li><li>Merging vs Rebasing</li><li>Lifetime Learning</li><li>Test coverage</li><li>Format on Save (1)</li><li>Rollbacks</li><li>Feature branches</li><li>Shelving (2)</li><li>Commit conventions</li><li>JSON data</li><li>Switch</li><li>gitignore (2)</li><li>Key management</li><li>Presentations</li><li>Null safety</li><li>Futures</li><li>pubspec.yaml</li><li>Minimizing state</li><li>"late" variables</li><li>Flutter template</li><li>Pop</li><li>Prepping for internship</li><li>Professionalism</li><li>Resume crafting</li><li>Separating classes</li><li>Android manifests</li><li>Overflow boxes</li><li>Team communication tactics (1)</li><li>Public accessibility / licensing</li><li>Group discussion (3)</li><li>Teaching a lesson</li><li>README files (1)</li></ul></div><p></p>Paul Gestwickihttp://www.blogger.com/profile/00684898302302604274noreply@blogger.com0