QZ qz thoughts
a blog from Eli the Bearded
Tag search results for games Page 1 of 10

NOTINT


One of the better Tetris clones for the Unix terminal enviornment is TINT, a recursive acryonym for TINT Is Not Tetris. (There is more than one Tetris clone by that name, apparently obvious joke is obvious.) Clean C code, good responsiveness, color output (curses "text UI").

I bring all that up, because a couple of years ago I had a vision for changing some of the rules of Tetris to see how the game would play. To do that, I found an open source version of the game and started hacking on it. The one I first found was the version in the BSD Games package. The resulting new game I called Eastris. It showed potential, but the game was not responsive at speed. Digging I found the original code was based on an IOCCC winner. So the original source was more about size of code than playability.

Eastris screenshot

Score: 9                   ww                   ww
Status: 11 - 0             ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww    wwww           ww
                           ww  wwww             ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           ww                   ww
                           wwww ww ww      ww   ww
                           wwwwwwwwwwwwwwwwwww  ww
                           ww  ww ww  wwwwwwww  ww
                           ww  wwwwwwwwwwwwwwwwwww
                           wwwwwwwwwwwwww  wwww ww
                           wwwwwwwwwwwwwwwwwwwwwww

   j - left   k - rotate   l - right   <space> - drop   p - pause   q - quit

Then I found TINT, and it plays better and has nicer code. My derivative of that I'm calling NOTINT, for "Not TINT". Besides the "easytris" mode, I've modified it to have several other play options. But maybe a discription of what easytris is would be good now.

Standard Tetris has a blocks that fall and stack. You complete rows to clear the board area, and you strive to never run out of space at the top. Easytris has all those same features. It even has the same shapes. What it doesn't have, and what made me call it "easy," is very random piece distribution. Instead you get mostly the same peice over and over again for a while, and then it changes to a random new piece to get over and over again. The way it is supposed to be easy: you have a good idea of what's coming, next, and after that, and after that. So you can plan your moves long in advance.

In practice, most of the pieces are pretty difficult to use to clear the board when you get 25 of the next 30 pieces all the same. Only the 1x4 piece is really easy. The 2x2 square piece seems simple, but is extremely unforgiving of mistakes. The T shape and the two variants of L shape are tricky, but with care can be used to clean up a board. The two S-jog shapes can maintain an equalibrium, but cannot really clean things up. Playing easytris really sharpened my Tetris skills, even if I did get bored after a while.

One that I started to notice playing a lot of tint style Tetris is that scoring is dominated by how far you drop pieces. It's much more important than clearing lines to the score. So I started adding new play modes to notint.

First was "zen mode" where the game speed never changes (unless you ask it to) and the scoring is 100% about lines cleared. Next I added a "speed mode" that makes scoring a function of lines cleared over time, but to be honest, I'm not very satisfied with that scoring and I may rework it.

After that I added "challenge mode". This has makes levels mean something different. Each level starts with a set of blocks that you have to clear. Those blocks are all incomplete lines, some levels have random patterns and some have predetermined patterns. Some levels have traditional random pieces and some have "easytris" inspired preferred pieces. Whatever the pattern or drop ratios, once you clear all the challenge blocks away, the board resets for the next level.

Notint in easytris mode screenshot


 Your level: 6             <>                    <>            STATISTICS
 Full lines: 138           <>                    <>
Blocks togo: 50            <>      YYYYYY        <>       MMMMMM        -    30
  Score  2536              <>        YY          <>       MM
                           <>                    <>           RRRRRRRR  -    30
                           <>    YY  YY          <>
    H E L P                <>  YYYYYYYYYY        <>       WWWWWW        -    47
                           <>      YYYYBBBB    YY<>           WW
 p: Pause                  <>      YYYYBBBB  YYYY<>               GGGG  -    26
 j: Left                   <>YYYY  YYYYYYYYCCCCYY<>             GGGG
 l: Right                  <>    YYYYYYYYYYYYMM  <>       CCCC          -    18
 k: Rotate                 <>YYYYYYYY  YYYYYYMMMM<>         CCCC
 s: Draw next              <>  YYYYYYYYYYYYYYYYYY<>               BBBB  -    27
 d: Toggle lines           <>YYYYYYYYYYYY  YYYYYY<>               BBBB
 a: Advance level          <>  YYYYYYYYYYYYYYYYYY<>       YYYYYY        -   184
 q: Quit                   <>YYYY  YYYYYYYYYYYYYY<>         YY
  SPACE: Drop              <>YYYYYYYYYYYYYYYY  YY<>       ---------------------
                           <>YYYYYYYY  WWWWWWYYYY<>       Sum          :    362
   Next:                   <>YYYYYYYYYYYYYYYYYY  <>
                           <>++  ++YY++YY++YY++GG<>       Challenge    :   5
                           <><><><><><><><><><><><>       Other blocks : 110

This has been my focus of late. All of the predetermined patterns I have been able to clear in random piece drop mode, but some are really hard unless you get lucky with your drops. After a while, I started to feel those were unfair, which is why I added less-random piece distributions. The really hard ones were the tall single-spaced bars and the two (one tall, one short) checkerboard patterns. Above, I'm failing pretty hard on one of those checkerboard levels with just a single challenge row left to clear.

NOTINT source at github.

Game Tools


Here, some discussion of two game tool programs I have in game-tools on github.

asciimapper

In the mid-1990s, I knew an admin of the Tsunami MUD and played the game a bit. Fast-forward a decade and I decided to give it a try again. At (then) about fifteen years old (now closer to thirty), it was one of the older MUDs around, which meant it had a very long time to expand. There were vast areas of the game to explore, and I set out to see as much as I could.

Over the course of several months, I visited huge swaths of the game, and got myself on the explorer leaderboard, where I was one of the lowest level characters there. (Accounts automatically delete after time time if you don't log in, so I can't know if others had done better than me before then, and you won't be able to find me there now.) Eventually I started to run into time-to-new-area payoff diminishing returns and stopped playing.

While I was playing I drew myself a lot of maps. At first these were on paper, but eventually I developed an ASCII art short hand. This let me have text files I could grep for noteworthy items or places. From there, I wrote a tool that could take my ASCII art maps and convert them into nice printable maps. asciimapper worked by converting my ASCII art into config files for ifm the "Interactive Fiction Mapper", which was designed for Infocom and similar games. The crossover to MUD maps was trivial. Some of the maps I printed and would hand annotate for further details, but most I kept only in ASCII file form.

I have all my ASCII art maps for Tsunami somewhere, I could probably dig them out and put them on the web. I haven't played in at least a decade now, though, and there's more than zero chance some of them are obsolete. Some became inaccuate while I was playing. In particular I recall the entrance to Toyland moving, to be friendlier to low level players.

I've been thinking about asciimapper again as I play "Andor's Trail"; (previously dicussed about a month ago here). In "Andor's Trail", there are perhaps 520ish visitable areas, most of which show up on the World Map, but about 20% are indoors, underground, or otherwise not visible there. How to get to those plus the inventories of stores in particular spots has been something I've been mulling over. The ASCII art needed for the World Map would be doable, but something of a challenge.

The maps are text form already though, just not very clear text form. Here's an excerpt from AndorsTrail/res/xml/woodsettlement0.tmx, an XML file apparently created by Tiled:

 <objectgroup name="Mapevents">
  <object name="east" type="mapchange" x="928" y="224" width="32" height="64">
   <properties>
    <property name="map" value="roadbeforecrossroads2"/>
    <property name="place" value="west"/>
   </properties>
  </object>
  <object name="woodhouse1" type="mapchange" x="608" y="288" width="32" height="32">
   <properties>
    <property name="map" value="woodhouse1"/>
    <property name="place" value="south"/>
   </properties>
  </object>
  <object name="woodhouse2" type="mapchange" x="640" y="128" width="32" height="32">
   <properties>
    <property name="map" value="woodhouse2"/>
    <property name="place" value="south"/>
   </properties>
  </object>
  <object name="woodhouse0" type="mapchange" x="224" y="256" width="32" height="32">
   <properties>
    <property name="map" value="woodhouse0"/>
    <property name="place" value="south"/>
   </properties>
  </object>
  <object name="sign_wdsetl0" type="sign" x="800" y="256" width="32" height="32"/>
  <object name="sign_wdsetl0_grave1" type="sign" x="128" y="160" width="32" height="32"/>
  <object name="sign_wdsetl0_grave2" type="sign" x="128" y="224" width="32" height="32"/>
 </objectgroup>

You can easily see how the map pieces connect together, including ones like woodhouse0, woodhouse1, and woodhouse2 that don't show up on the World Map. In woodhouse2.tmx we find Lowyna:

<objectgroup name="Spawn">
  <object height="96" name="smuggler1" type="spawn" width="96" x="32" y="96"/>
  <object height="128" name="smuggler2" type="spawn" width="96" x="128" y="96"/>
  <object height="32" name="lowyna" type="spawn" width="96" x="288" y="96"/>
[...]

Which with a little bit of work we can connect that the shop "droplist", in this case in AndorsTrail/res/raw/droplists_v070_shops.json, to get items she stocks.

A map.tmx to IFM format converter might be handy, but I haven't put any serious thought into it.

asciimapper

I have thought about game play efficiency with "Andor's Trail". In particular while playing I thought it would be useful to have a way to see how fast I'm earning in-game rewards like XP, game currency, item drops, and how fast I'm using consumables while doing so. I imagined a tool that I could tell what I have at a particular time and it would work out how much that changes over time.

Those imaginings lead to stat-timer, a CLI with a very old school interogation interface. You can use the command line to give it starting stats or just start it and it will ask for stats. Then you can update as many or as few stats as you want each round and it gives updates. The design requires that you name stats for the initial state, and then if in same order, you can omit names. Thus the most important things being measured should be first, and least important last. Or least changing last.

In practice this means I've been putting XP first, then common area item drop and/or gold, then health potion count, and then rare drops, and finally — sometimes — constants I want for annotations. As I play, I update XP frequently and other columns less frequently. To update just the first two columns is a matter of just entering the first two numbers. To update first and third requires labeling the number for the third column. After each entry it gives a snapshot of how things are doing on a per-second basis. When done, I can <ctrl-d> out or put a ! at the end of the numbers to indicate final update. It then gives a final update with total changes, per-hour and per-second rate of changes. This makes it easier to compare play style one to play style two even if they are on different days and for different lengths of play.

If I update it further, things I've been thinking about for improving it include: a curses interface with data at particular screen locations, sophisticated "pause timer while entering data", realtime per-second updates, and perhaps a more sophisticated state model for the command line, for better continuation after an intertuption.

ascii-art

Roguelike


A few weeks ago I read an article at Ars Technica on the history of roguelike games. It certainly brought back memories.

Sometime, probably in 1993 or 1994, I found and got very into playing Moria, one of the major early roguelikes. I've played that game on a variety of computers and computing devices over the years. It was the impetus for learning how to write keyboard marcros for me in the mid-1990s. I delved into the code in the late 1990s and early 2000s. My personal version (with a very lame webpage) has a targeting patch, for more accurate spell / thrown object attacks, which I didn't write but found and applied, plus changes to the map generation that I did author. There might be other changes in there that I don't remember.

The Magic Missile strikes the Giant White Mouse.  -more-
                                                             ############ #
Elf                ##########                                           # #
Mage               #        #                                           # #
Apprentice         # ###### #                             ############### ###
             ####### #    # ###############################.........rrr##   ###
STR :      6         #    #                      '       .@..........rr#### ##
INT :  18/59 #########    ##########       :     ##########.........rrr#  # ##
WIS :     13                    #### #   #########        ########.#####  # ##
DEX :     14                #####    #                                    #
CON :      9                #     ####                                    ###
CHR :     17                # #%##%                                         %
                       %#%%%%.%%####%#%                                    %%
LEV :      2           %..............%%%#%%%%%                           %%
EXP :     21           #..............'   ' ' % %                         # +
MANA:      5           %..............#%#%%#%   #                         % #%%
CHP :     14           ....!..........#       # ##################+#####%## #
                       #..............#       #                    '        %
AC  :      9           ################       ########################%###%##
GOLD:      1

                                                                 50 feet

Yikes! That's a lot of mice for a level two character to face. Those guys breed fast.

{"Giant White Mouse"        ,0x0020000AL,0x00000000L,0x2072,    1,  20,
   8,   4, 11, 'r', {1,3}   , {25,0,0,0}                ,    1}

The Giant White Mouse in code form. 0x0020000A: 0x00200000 "explosive" breeder, 0x00000008 20% random movement, 0x00000002 normal move and attack; 0x00000000: knows no spells; 0x2072: 0x2000 can be seen with infra vision, 0x0070 hurt by fire, frost, and poison, 0x0002 hurt by slay animal. It is worth at most 1 xp; tends to sleep a lot; but can notice the player from 8 squares away; has an armor class of 4; moves at speed 11, out of a range of 9 to 13; is shown with letter r; starts out with 1d3 health; has only one attack, #25 in that table (which is {1,2,1,2}: a normal attack, described as "bites you", that does for 1d2 damage); and is typically found on dungeon level 1.

I also played it extensively on an early Palm Pilot. No keyboard on that, you had to use a stylus to scribble special shorthand in to enter letters. A novel input system at the time, but it certainly changed mcb to cast the second spell from the third spell book into more of an adventure. But it allowed me to play the game on the go. My daughter was born in the early 2000s and my wife was attending graduate school at the time. I frequently would have an afternoon hour or two in a car with a napping baby waiting for my wife to finish a class. Moria on the go was just the thing.

A decade or so later I got a PocketCHIP hackable computer. The "CHIP" part of it was designed as a Raspberry Pi alternative better suited for embedding in portable hardware. The goal was (a) under ten bucks, (b) built-in storage, (c) built-in bluetooth and wifi. This way you could add $20ish in other parts and have a product you could reasonably expect to retail at $90 to $100. Raspberry Pis at the time were $25, with no built-in storage, blue tooth, or wifi but did have built-in HDMI and ethernet, better for non-portable uses like living behind your TV. Anyway, the "Pocket" part of the PocketCHIP is an add-on board that includes a battery, touchscreen, and keyboard, and was at least partially aimed at being a portable hackable game system. It has a terrible keyboard and a small display, but it does score high on hackable. (The company went out of business, but you can still find new-old-stock on ebay.) Discussion about the system on the official forums went to roguelikes, and I tried Moria. It didn't work because Moria is strongly tied to an 80x24 screen, but then I remembered the Palm version got around that. I tracked down the Palm version source code and started to patch Moria for PocketCHIP sized screens. I never worked all the bugs out before someone else found a way to get 80x24 with an alternative terminal program. That was the last time I delved into Moria source.

There doesn't seem to be a Moria port for Android, although it is playable in Termux. The virtual keyboard play method is even less fun / forgiving than the "Graffiti" shorthand of PalmOS, so that's not a lot of fun. The comments on Ars Technica article pointed to a well-liked roguelike game developed specifically for Android, Andor's Trail which, while still an incomplete game, is rather fun to play.

Gornaud mines battle
You are affected by Weak Poison (2 rounds)
Scaled venomfang hits you for 0 hp!
Your attack misses
    {
        "id":"scaled_venomfang",
        "name":"Scaled venomfang",
        "iconID":"monsters_snakes:3",
        "maxHP":35,
        "maxAP":10,
        "moveCost":5,
        "monsterClass":"reptile",
        "attackDamage":{
            "min":2,
            "max":4
        },
        "spawnGroup":"gornaud_2",
        "droplistID":"cave_serpent",
        "attackCost":3,
        "attackChance":150,
        "blockChance":90,
        "damageResistance":2,
        "hitEffect":{
            "conditionsTarget":[
                {
                    "condition":"poison_weak",
                    "magnitude":1,
                    "duration":2,
                    "chance":"50"
                }
            ]
        }
    }

It's open source and I've perused some of the source. JSON data structures for items and monsters are so much different (notably verbose, thus faster to read) from the C struct tables of Moria. I'm still not sure how the quests actually work, although I have seen the data structure for how quest messages and NPC dialog for quests is stored.

It makes me want to write a new game again. I've long thought about forking Moria into a different game with a more modern setting. I have a rough idea of the code modernizations I'd like to apply.

  • Remove the excess of #ifdef for compiling on various non-Unix systems and similar #define messiness.
  • Track monsters individually allowing them to have actual inventory and preventing monster overflow (the "Compacting monsters" message).
  • Make level generation predicable for a given game, so that you always have the same places. Right now only level 0 is predicatable.
  • Greatly curtail mining, because not-a-mine anymore.
  • Greatly expand the use of "traps" to move between maps. Moria currently uses staircases up and down to move between levels, and traps to implement entering stores.
  • Allow stores to use more screen space if available. Increase store inventory.
  • Enable function pointer callbacks on items and monsters for rare special effects. Enable function pointer callbacks on traps for map movement.