Friday 2 August 2013

Schaak! Post-Vivem



(Cross-posted from http://www.ludumdare.com/compo/2013/07/29/schaak-post-mortem/, renamed because it didn't exactly die)


Schaak!, my 7dRTS for MiniLD 44, is complete and submitted. Coding started at 9pm on Monday the 22nd, and ended at 9pm on Monday the 29th. Very little of the process went to plan. The repository is online at https://github.com/essarrdee/Schaak.

DAY 1

Day 1 was mostly taken up with scribbling and typing design ideas, and emailing my collaborator about CMake, ideas, and names. Various bad puns and portmanteaus were rejected without comment, Persian and Spanish words were considered, and a Dutch word was settled on. Coding started at 9pm, and mostly consisted of copying SFML boilerplate from a previous project.

DAY 2

Coding continued into the night. Board drawing was set up pretty quickly, followed by board drawing with cover indications (showing whether a square is guarded by black or white pieces). I took an unusual approach to board rendering - a texture the size of the map is stored on the graphics card, and updated regularly with the cover information. The board size had to be halved to fit this texture in my CPU-GPU bandwidth.

After a sleep, scrolling to zoom was implemented in the evening and piece definition files were planned - just a name, sprite information, move speed, and list of move/capture locations.

DAY 3

Implementing the piece data loading was pretty quick. Next I made a dummy piece, the Doomed Wanderer, that jumps around the map in Knight moves and tests the protected-square display's correctness. With one piece working, it was time to plan how thousands of pieces would work - not a trivial consideration when all of them will be AI controlled. The plan was to exploit the grid nature of the board - if you need to know whether a piece is in a square, you just look up the ID of the piece in that square. This simplifies capturing and collision avoidance. I also wanted to keep the vector of units compact, minimise the number of gaps formed when pieces die, and minimise the time it takes to spawn a new piece.

With the help of some people from the IRC channel, I settled on a three-part structure to hold pieces:
The vector of piece objects, indexed by piece ID which never changes during a piece's lifetime
The double-ended queue of free slots in the piece vector, which just lists all the piece IDs that don't correspond to a live piece
The 2D array of board occupants, mapping each square to the ID of the occupying piece (or -1 for no piece). Pieces also store their own board coordinates.

With the first two of those implemented, the Doomed Wanderer met his fate - deletion. I now had random walkers spawning a few times per second. There was no collision detection without the occupancy grid, though.

DAY 4

I had been making attempts to recruit artists to draw chess piece graphics since day 2, but everyone either joked about how their pixel graphics skills were so good that they could draw 1x1 sprites, or made an attempt and gave up. Or told me to use a font that doesn't scale small enough, or attempted to shrink vector sprites down to 8x8 and smaller (with antialiasing).

Halfway through the challenge and I only had a board full of invisible random walkers. There was no way around it, I had to make the sprites myself, today.

It took about 3 hours to get them right. I looked at low-resolution chess sprites from around the internet for inspiration, and for the larger ones visualised my grandfather's chess set. The knights look pretty stupid. Drawing pixel horses is difficult.

The high-resolution sprites have outlines, and to smooth out the zooming I made the 16x16 ones have half-outlines. They looked *terrible* like that. I added a bit more outline to them, but I'm not really sure what to do with them.

Code written today: zero lines. Halfway through the challenge, I only had a board full of random walkers and a spritesheet.

DAY 5

Progress towards drawing sprites. Still no chess pieces on screen. Time to get concerned about whether I'll finish the game.

DAY 6

Continued coding into the night. First thing accomplished was pieces being drawn. Squashed bugs. Got a linux makefile from a new collaborator, who exists. Implemented scrolling. Decided it would be a bad idea to develop epilepsy while coding an RTS, set scrolling speed to prevent horrible flashing. Made pieces mortal. Made a video demonstrating the zoom functions and piece sprites: http://www.youtube.com/watch?v=XIKouIOXPtY

Pieces are mortal - time to make them capturable! Planned and implemented the generic piece AI, capable of 4 out of 5 of the behaviours I want (the "protect" behaviour requires an extra, more difficult feature). Loaded behaviour definitions from files (but I only wrote two of the 4 behaviours). Took me a while to notice that the value-based AI was actually picking its *least* favourite move. Discovered this because a lot of pieces set to "Kamikaze" were ignoring all opportunities to capture enemy pieces...

Next problem with the generic AI was that the move-choosing method sorted by target coordinates when values were equal. This meant that *everyone* beelined for the bottom-right corner of the map. And captured their friends. Fixed. I added a behaviour that causes pieces to seek friendly pieces' cover, which made them form clusters. Made a video of that: http://www.youtube.com/watch?v=ght5eUgH3QM. I changed the friendly/enemy cover colour scheme from green/red to green/magenta to improve colourblind usability.

6 days into the 7 days challenge, I had exactly zero ways of letting the user interact with the pieces. This needed to change, and the first thing to do was implement selection and commands. Dragging boxes with the mouse is pretty awkward on a scrollable zoomable map, but I managed it. Set selected pieces to flash regularly. I didn't have time to implement complex things like orders to attack specific units or groups of units, so the only command you can give (and really the only command you need) is to set a destination box by right-clicking. The distance from the destination box feeds into the unit's value system and lets them follow orders - within reason (trying to stay out of enemy-covered squares, taking capture opportunities, etc.). I made a video of the epic battles possible with this command system: http://youtu.be/LA75nbdoZU4

At some point there was some sleep. I also started ignoring the TODO list and scribbled down a list of priorities for giving the game some semblance of strategic depth on the last day.

At some point this day or yesterday I decided that playing this game against an AI was a terrible idea, and that playing this game over a network was not something I could code in 2 days. It had to be local multiplayer. Switching control between black and white and playing against oneself or a real-life friend.

DAY 7

Epic battles are only meaningful if you can tell who came out on top. Added piece counters for both players.

Added an interface, the screen looks so much nicer at minimum zoom now. Buttons to select all units (which gave me trouble when I recorded the previous video), or to select all units of a given type, or to restrict the current selection to units of a given type. 20 minutes left.

Added buttons to switch pieces between behaviour types, with no feedback of any kind as to what behaviour they now follow. Added a key to delete pawns and earn money, and buttons to upgrade piece speed and pieces' chance to cheat death. Again, no feedback. 5 minutes left. No chance of adding feedback for these features now. Users will just have to keep track themselves...

Updated the readme, took screenshots, submitted.

I really want to add those few sf::Text objects to make the game more intuitive. Maybe a button to display the controls in-game. This will all have to wait for a non-7dRTS update.

Some other post-Monday day


Made some minor changes to respond to vague criticism. Added a help button that describes the objective and lists controls; made the upgrade/AI buttons display their current values; slowed down both movement and production of pieces, and limited production of pieces to their Kings' side of the board.