The Children

Copyright © 2021 by Víctor Parada

The Children

This is a little game for the 2021 NOMAM's BASIC 10-liners Contest. This program fits in the EXTREM-256 category, and it was written using FastBasic 4.5.2 for the 8-bits ATARI XL/XE. Development started on 2021-01-26, and it took 3+10+1 days. The final version's date is 2021-03-08.

UPDATE: It obtained the 1st place of 25 entries in the category.


Description

Pedro has been commanded to rescue the child, but he realized that there are many of them!!!


Instructions

CHILDREN start Press the button when the title scren appears.
CHILDREN move Find a way to reach your target. Yellow rocks can be pushed to clean your path.
CHILDREN ladders You can move up and down through ladders, or you can securely fall from any height, but you cannot jump upwards.
CHILDREN target To get a target, you must approach to them only by the side. You can remove dirt to find a path, but be careful: if there are objects above, they might fall and block your path.
CHILDREN trap If you think that you fell in a trap, press the button to restart the zone. There is no backtrace to undo your latest movements.
CHILDREN many Some zones have many targets, and you must get all of them to clear the zone.
CHILDREN end The game is over when all the zones were cleared. If you failed in some zones, a message will appear with the number of times you had to repeat a zone. If there is no message, congratulations, you did an excelent work! Press the button to start all again.

Development of the game

From time to time, some game genres became popular, and ports or new development appears for some console or computer, and sometimes I think that it could be simplified to be programmed as a tenliner. During the first half of January, I had to test some games for the ZPH's Annual Homebrew Awards 2020, and some of the games were "Pitkat" for the Atari 2600 and "Millie and Molly" for the Atari 7800 consoles. Both were based on games that evolved from the game "Pitman" or "Catrap", a game written in BASIC for the Sharp MZ-700 computer in 1985. I didn't know this fact when I decided to try a simplified version for the tenliners contest of the ones I tested.

Pitkat

Pitkat

Millie & Molly

Millie & Molly

I started from scratch in FastBasic, because I wanted to use some graphical tricks I previously tried to get a bigger color palette for Atari's graphics mode 2 (ANTIC 7 mode). After some hours I had a working engine for a 20x10 playfield. I had to build a tool to pack the puzzle data in order to be included in the source listing and displayed on screen. The compresion method I used was a simple RLE algorithm (Run-Length Encoding), where each byte of data represented an element of the screen (3 bits to map up to 7 different elements) and a repetition count (5 bits for 32 values), where zero times meant end-of-data. The first two puzzles were designed to test all the game logic in the engine, and I had to add a lot of delays in different portions of the code in order to make the animations be fluent.

CHILDREN prototype

Prototype with a demo puzzle.

CHILDREN prototype

Puzzle to test the engine at all the transitions.

I decided to change the screen resolution to 16x10, because the puzzles won't be that big and also to save data bytes in order to include as many puzzles as I could in the 10 lines of code.

The next step was to replace the characters I was using with user-frendly bitmaps, and I had to design them. I used a spreadsheet with formulas to generate the required source code to be copied and pasted into the BASIC program. I tried many 8x8 sprites in the search for a theme for the game, like to kill monsters, to collect objects or to save people.

CHILDREN prototype

Bitmaps!

CHILDREN prototype

First proto.

The first prototype with images felt "heavy", so I decided to use tiles of 7x7 pixels, leaving 1 pixel free between rows and columns, and that gave a nice touch to the puzzle. When it was time to define the colours to be used, I used the DLI feature to build multicolor or shaded sprites. As the main character looked like the Mandalorian, I tested how it would look like. After some interations and sprites adjustments, I got a theme for the game.

By that time, more sounds were added and the game timming adjusted, and also a small animation of the target's arms. I had to modify my developing tools in order to be able to include the levels in the FastBasic source code every time I add or change them for testing.

CHILDREN prototype

Got a theme!

CHILDREN prototype

Using DLI to colorize

During the following days, I created some puzzles. As it was a simplified version of the previous games, all the puzzles should be created to be solved by one character, and there was only one type of target: the one that falls by gravity. As I defined a list of objects and their attributes, the cost to add a floating object was just to add an object to the list and assign a bitmap to it. All the game logic was based on the attributes and it worked so good. New type of puzzles could be added. I managed to define the tile/sprite using the same asigned colors from the other target.

When I fill all the available coding space with puzzles, I sent a copy of the game to DMSC (FastBasic's author), and he suggested me to use a variant of LZSS compression algorithm instead of the RLE method I was using, because I could save enough space to add more puzzles. This algorithm used data bytes to store a literal (a single 3 bit value) or an offset/amount pair of 4 bits values to copy previously decompressed data. Doing a comparison of the compression ratio between algorithms, puzzle by puzzle, it was clear that some of the puzzles had a better compression ratio using one method than the other, and it was not always the same algorith. I tried to combine both compression methods into a mixed one and, after a brainstorm, I got an algrithm that behaved pretty well, better than both methods by themselves, and I got even more space for puzzles.

The new method for compressing puzzles splits data bytes in three portions: 3 bits for the A component, 4 bits for the B component, and 1 bit to signal the meaning of those components, which it is:
- Bit=1: compressed as RLE, stores A value repeated B+1 times (1 to 16)
- Bit=0: compressed as LZSS, copies A+2 bytes (2 to 9) from an offset of B+2 (2 to 17) to the back.
The bits were sorted in a way that the decompressing algorithm to be the shortest one, and that was B+signal+A. I also had to reassign the order of the objects in order to avoid the $9B value in the data, which it cannot be typed inside a string as it is the new-line character.

I could finally pack 17 levels (zones) in only 3 lines of FastBasic code, and used some extra space from the remaining 7 lines of code to include an aditional animation for the player: he walks.

I wanted some feedback about the game and the levels, so I made a special build and shared it with some friends. After some days, it was clear which levels were the most difficult and which ones required some adjustments.

In the meanwhile, DMSC poosted in AtariAge that a new version of FastBasic would be released soon and requested for beta testing. The main new feature was the ability to pass parameters to procedures, and as one of the procedures of this game required to assign the parameter to a global variable, I changed all the invocations to that procedure, which it also saved some more coding space. After some other suggestions about abbreviations I sent to DMSC that were included in the latest beta of FastBasic 4.5, I could save another 50 bytes of coding space, enough for another level.

With the feedback and the extra space, I modified some levels, changed the order of them, and added new levels: the game reached 20 zones in total.

DISCLAIMER: Neither George, Walt, nor their people have supervised this development.


Download and try

Get the CHILDREN.ATR file and set it as drive 1 in a real Atari (or emulator). Turn the computer on and the game should start after the loading completes. A joystick in port 1 is required.

NOTE: This game is for PAL computers. The ATR includes "NTSC.XEX" to be played on NTSC computers. I will run a bit faster than normal, but you will get the right colors.


The code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:


THE CHILDREN

(c) 2021 Víctor Parada G.
move adr("{binary data}"),$820D,239
move adr("{binary data}"),$811F,239
move adr("{binary data}"),$8030,240
move adr("{binary data}"),$7FFF,50
Maps data
There are the following blocks
- Number of targets for every level: 1 byte per level.
- Starting position of the player: 1 byte per level.
- Pointers to start of level data: 2 bytes perl level, plus 2 bytes for a final end-of-data vector.
- Compressed map data using a custom combination of LRE and LZSS algorithms. Each byte of data has 3 components: 4 bits for B, 1 flag bit and 3 bits for A, interpreted like this
> if flag bit is 1, it was RLE compression, with the A value repeated B+1 times (1 to 16).
> if flag bit is 0, it was LZSS compression, with an offset of B-2 (-2 to -17) to copy A+2 bytes (2 to 9).
Chuncks of data are stored in memory in reverse order to overwrite the extra leading byte from FB strings.
t=adr("{binary data}")+1
General data
- Tiles and their colors (8 bytes)
- Properties (8 bytes)
- Player shapes (4 bytes)
- Playfield colors (708-712) (4 bytes + 1 reused byte)
- Bitmaps (72 bytes for 9 tiles)
$07->7 Player (to the left)
$08->0 Player (back)
$09->1 Ladder
$0A->2 Rock
$0C->3 Sand
$0B->4 Wall
$0D->5 Target
$0E->6 Flotating target
$0F->7 Player (to the right)
- Title screen (12 bytes)
- Bitmap for the target's arms in wide position (2 bytes)
- Bitmaps for legs of the player (8 bytes)
- Game over text (10 bytes)
e=t+8
Property bits
0-1: free
1-2: walkable
2-4: falls
3-8: pushable
4-16: target
5-32: climbable
o=e+8
Player types
0: space
1: ladder
2: rock
3: brick
4: sand
5: target
6: floating target
7: player
move $E000,$7000,512
Builds a new charset in $7000
move o+8,$7038,72
Modifies chars 39 to 47 ($27 to $2F) with the tiles bitmaps
graphics 18
Sets up graphics mode 2 (ANTIC 7) without text window
poke 559,33
Sets the screen width to 16 instead of 20 chars
move o+4,708,5
Sets the color palette
poke 756,$70
Enables the new charset
h=16
Constant (to save bytes)
r=dpeek(88)+32
Playfield area
mset dpeek(560)+6,10,$87
Enables DLI for playfield area
dli set d = $0C wsync into $D016,
  $AA wsync into $D017,
  $36 wsync wsync wsync into $D019,
  $E8 wsync wsync into $D017,
  $8A wsync into $D016, $38 into $D019,
  $E4 wsync into $D017,
  $36 wsync wsync into $D019,
  $78 wsync into $D016,
  $34 wsync into $D019
Defines DLI code
dli d
Activates DLI
move o+80,r+50,12
Title screen
do
MAIN LOOP
  l=0
Sets starting zone (minus 1)
  i=0
Crears the number of retries
  while strig(0)
  wend
Waits for the next game

GAME LOOP
  while l<20
Repeat for every zone:
    q=1
Clears the player retry flag (inverted)
    position 5,0
Points to top for the zone number
    exec w
Draws the zone
    print #6,"ZONE ";l+1
Prints the zone number on top
    x=r+peek($8015+l)
Gets the initial player position in current zone
    n=peek($8000+l)
Gets the number of targets in the zone
    z=0
Player does not start in a ladder
    k=0
Sets the player shape as looking to the right
    v=0
Resets the bad movement counter

ZONE LOOP
    while n * q
Repeat the loop until there are no more targets or the player quits it (pressing the trigger)
      j=15-stick(0)
Reads the joystick
      u=(j=8)-(j=4)
Detects horizontal movement
      if u
Moving horizontally?
        k=u<0
Detects the direction to update the player shape
        y=x+u
Finds the position where the player wants to go
        a=peek(y)&7
Gets the tile in the destination
        if peek(e+a)&2
Walkable?
          poke x,peek(t+z)
Removes the player from the old position, drawing a ladder if the player previously entered into it
          z=a
Saves the content type at the new position
          if z<>1
Not entering a ladder?
            if z>=5
Target found?
              dec n
            endif
Decrements the target's count.
            z=0
          endif
Destination was not a ladder
          exec s
Plays a sound based on the destination
          m=x-h
Gravity check over last position
          x=y
          exec p
Move to the new position
          exec f x
Gravity check at new position
g=x
          exec f m
Execute gravity at saved location
g=m
        elif peek(e+a)&8 and peek(y+u)=0
          exec p
Pushable? Is it free on the other side?
          exec s
Plays a sound based on the destination
          poke y,0
          poke y+u,peek(t+a)
Moves the object
          exec f y+u
Gravity check bellow the object in its new position
g=y+u
          exec f y-h
Gravity check where the object was
g=y-h
        else
          inc v
Count bad moves
          exec p
        endif
Updates player side, in case he is looking to the other side
      elif j=2
Moving down?
        y=x+h
Finds the position where the player wants to go
        a=peek(y)&7
Gets the tile in the destination
        if a<2
Is there a ladder or an empty space?
          poke x,peek(t+z)
Removes the player from the old position, drawing a ladder if the player previously entered into it
          g=x-h
Gravity check over last position
          x=y
Saves the new position
          z=a
Saves the content type at the new position
          exec s
Plays a sound based on the destination
          exec p
Moves to the new position
          exec f g
Execute gravity at saved location
          exec f x
g=x
        else
          inc v
        endif
Count bad moves
      elif j=1
Moving up?
        y=x-h
Finds the position where the player wants to go
        a=peek(y)&7
Gets the tile in the destination
        if a<2 and z
Is there a ladder or an empry space while already in a ladder?
          poke x,peek(t+z)
Removes the player from the old position, drawing a ladder if the player previously entered into it
          x=y
Saves the new position
          z=a
Saves the content type at the new position
          exec s
Plays a sound based on the destination
          exec p
Moves to the new position
        else
          inc v
        endif
      endif
Count bad moves
      if v>2
Too many bad moves?
        v=0
Resets counter
        sound 0,10,12,4
        pause 1
        sound
      endif
      pause 5
Whistle!
      dpoke $706C,
        dpeek(o+60+peek(20)&32)
Animate the Child's arms
      q=strig(0)
Player wants to reset the zone and try again?
      if j
        poke 77,0
      endif
Clear attract mode if the joystick was moved
    wend
End of ZONE LOOP
    if q
Does the player want to repeat the uncleared zone?
      for a=0 to 64
        sound 0,99-(a mod 10)*3,10,8-a/8
        pause
      next a
Whistles
      inc l
Moves to next zone
    else
Player wants to retry zone
      sound 0,201,12,4
      pause 9
Buzzer
      inc i
    endif
Increments retries counter
    sound
Shuts up any playing sound
  wend
End of GAME LOOP
  exec w
Last level is just a game over screen
  move o+102,r+99,10
Displays a "completed" message
  if i
The player had to repeat a zone?
    position 19,0
    print #6,i;" defeats"
  endif
loop
Displays the number of repetitions
proc f g
Subroutine to move in sequence some falling objects
  if g=x and z
    exit
  endif
Do not apply gravity to the player if he is holding a ladder
  b=g
Saves the starting position to check
  do
Repeats for every falling object over the one to check
    c=0
Flag to check if an object dropped at least one time
    a=peek(g)&7
Gets the object type at the checked position
    if peek(e+a)&4
Is it a non-fixed object?
      while peek(g+h)=0
        pause 1
Is there a space below it?
        c=peek(g)
        poke g,0
Removes the object from its current location...
        g=g+h
        poke g,c
And puts it again in the new location.
        c=8
      wend
Flags that the object was moved at least once
      sound 0,255,6,c
Plays a sound when the falling object touches another
      if a=7
        x=g
      endif
If the object was the player, sets its new position as the current.
      b=b-h
      g=b
      pause 1
Now, check the object that it was over the one that fell
      sound
Shuts up the crash sound (if it was enabled)
    else
      exit
    endif
It was a fixed object, no more checks are needed.
  loop
endproc
Next object...
proc s
Sound subroutine. Plays a sound based on the content of the player's new position before he steps in
  b=peek(y)&7
Gets the content of the destination area
  if b=0
    for b=0 to 3
      sound 0,y&1*4+40,10,3-b
    next b
Steps in open area
  elif b=1
    sound 0,(x-r)/h*2+80,10,2
    pause 1
    sound
Climbing (up or down)
  elif b=2
    for b=24 to 0 step -1
      sound 0,95,12,b/3
    next b
Pushing a rock
  elif b=3
    sound 0,8,8,3
    pause 1
    sound
Digging in sand
  elif b>=5
    for b=h to 0 step -1
      sound 0,b*3,10,b/2
    next b
  endif
endproc
Target was rescued
proc p
Puts the player in its current location, looking to the current side (or backwards if he is over a ladder)
  poke x,peek(o+k+z*2)
Draws the selected tile
  b=x&1*2+o+94
Selects the leg shape
  dpoke $703E,dpeek(b)
  dpoke $707E,dpeek(b+4)
endproc
Modifies the tiles with the selected shape
proc w
Subroutine to display current zone
  for c=9 to -2 step -1
    mset r+c*h,h,0
Clears the screen row by row, from the bottom to the top
    pause q
  next c
The refresh speed is faster if the player wanted to repeat the zone
  a=$802A+l*2
Gets the address of vector pointing to the zone data
  c=r
Sets the start of the playfield
  for n=dpeek(a) to dpeek(a+2)-1
Parses the zone map data
    a=peek(n)&7
    b=peek(n)/h+1
Bits 0-2 (a): Object type || Count of objects in sequence
Bit 3: 1=RLE || 0=LZ
Bits 4-7 (b): Repetitions || Pointer to recent similar objects
    if peek(n)&8
Not similar to previous data?
      mset c,b,peek(t+a)
Gets new data from source
      c=c+b
Updates screen pointer to next position to display
    else
Similar
      move c-b-1,c,a+2
Copies data from other just loaded
      c=c+a+2
    endif
  next n
endproc
Updates screen pointer to next position to display

Return to my 10-liners page.

© 2021 by Víctor Parada - 2021-02-14 (updated: 2021-04-10)