Mental Mania

This file is part of and ISTUG Public Domain Library 5. Download the collection to get this file.
Developer(s): James DuPuy
Date: 1984
Type: Program
Platform(s): TS 2068
Tags: Game

Tower of Hanoi puzzle game for five discs across three pegs. The player must move all five discs from peg 1 to peg 3 without placing a larger disc on a smaller one, within a 100-move limit. Disc graphics are built from block-graphic strings stored in a DIM’d string array d$(5,10), with each disc rendered in a different INK color using INK (6-P(N,O)). The game supports both keyboard input (keys 1–3) and joystick input via the STICK function, reading directional values 1, 4, and 8. A high-score tracker persists across replays, and completed move sequences can be printed via LPRINT and COPY.


Program Analysis

Program Structure

The program is organized into clearly labeled subroutines and sections separated by REM statements. Execution begins at line 1110 (initialization), which is reached via GO TO 1110 at line 20. After initialization, control flows to 140 (instructions/input setup), then into the main game loop starting at line 210.

  1. Lines 1100–1270: Initialization — POKE keyboard repeat, set high score, define disc strings, populate peg array, draw screen.
  2. Lines 30–130: Display subroutines — draw pegs (GO SUB 40) and draw all discs (GO SUB 60).
  3. Lines 140–190: Pre-game setup — instructions, joystick/keyboard selection, border drawing.
  4. Lines 200–530: Main game loop — move input, validation, array update, win check.
  5. Lines 540–680: Move validation subroutines — “FROM” check (GO SUB 550) and “TO” check (entered via GO TO 620).
  6. Lines 690–780: Joystick input handlers for source and destination peg selection.
  7. Lines 800–930: Win routine, move printing via LPRINT.
  8. Lines 940–1000: Lose/quit routine and restart prompt.
  9. Lines 1010–1090: Instructions and joystick briefing subroutines.

Data Representation

The three pegs and five disc positions are stored in a two-dimensional array P(3,5), declared as DIM p(PI,5) — an interesting idiom that uses PI (≈3.14159) as the first dimension, which is truncated to 3 by BASIC’s integer conversion. Each cell holds the disc number (1–5) occupying that slot, or 0 for empty. At initialization, all five discs are placed on peg 1: FOR N=SGN PI TO 5: LET P(1,N)=N: NEXT N.

Disc graphics are stored in d$(5,10), a string array where each element is a 10-character string composed of block graphics (\:: = █ and \ = space). Disc 1 is the narrowest (2 block pairs wide) and disc 5 the widest (5 block pairs), creating a visual taper. They are rendered with INK (6-P(N,O)), giving each disc a distinct color based on its disc number.

Key BASIC Idioms

  • SGN PI is used as a constant for 1 throughout all FOR loops (e.g., FOR N=SGN PI TO 5), saving a byte over the literal 1.
  • DIM p(PI,5) exploits the fact that BASIC truncates the floating-point value of PI to the integer 3 for array dimensions.
  • POKE 23609,25 at line 1110 sets the keyboard repeat speed (system variable REPDEL), reducing key-repeat delay for more responsive input.
  • The “TO” error-check subroutine is entered via GO TO 620 rather than GO SUB, meaning it falls through directly to GO TO 430 (store moves) — a structured-fall-through technique avoiding an explicit RETURN.
  • Move history is accumulated in the string variable M$ by concatenation: LET M$=M$+A$+"-"+B$+":", building a human-readable log for printing.

Joystick Support

Joystick input is handled via the STICK function (|), called as |(1,1) to read joystick port 1. Direction values used are:

STICK valueDirectionMapped peg
4Left1
1Up2
8Right3

When joystick mode is active (LET st=1), lines 690–730 and 740–780 handle “FROM” and “TO” peg selection respectively, branching back into the keyboard-input flow at lines 290 and 380 once a valid direction is detected. The X key remains the quit key in joystick mode.

Screen Layout and Graphics

The three pegs are drawn using PLOT/DRAW commands in the subroutine at line 40, with each peg consisting of a 4-pixel-wide, 42-pixel-tall vertical bar. The UDG character \a is programmed at line 1140 with the alternating byte pattern 170,85,170,85,170,85,170,85 (binary 10101010/01010101), producing a checkerboard or hatched fill used in the peg labels row at line 1240. An outer border rectangle is drawn at line 1260 and a second inner rectangle is added on win at line 800 using OVER 1 to erase the outer border cleanly.

Win and Lose Conditions

The win check at lines 480–520 iterates over peg 3 slots, incrementing S each time P(3,G)=G (i.e., disc G is in slot G of peg 3, meaning discs are stacked in correct order 1–5). If S=5, all five discs are correctly placed and the win routine fires. The 100-move limit is enforced at line 240; exceeding it redirects to the lose routine at line 950. The high-score variable hs is initialized to 100 and updated only when the player wins in fewer moves than the previous best.

Bugs and Anomalies

  • At line 70, the loop variable is written as lowercase n (FOR n=SGN PI TO 5) but the body references uppercase N (PRINT AT N,1,,). Sinclair BASIC treats variable names case-insensitively for numeric variables, so this works correctly.
  • Line 130 closes two nested loops with NEXT O: NEXT n — again mixing case for n/N, which is harmless.
  • The instructions text at line 1030 contains a grammatical error: “move all 5 of the discs one at a time” is split mid-word as “at a” on separate print lines due to fixed-width layout packing, but this is purely cosmetic.
  • At line 450, the condition IF N=1 uses N, but N at this point in the flow holds whatever value it last had in the display loop (line 80130). The intent appears to be checking whether the destination slot is position 1 (top of peg), but the variable being tested is the loop counter rather than the insertion index. The correct placement logic relies on the “TO” subroutine’s loop variable N from lines 630–670, which exits with N set to the first empty slot — making this effectively check if slot 1 is empty (first position on peg), which works correctly in practice but is fragile.
  • The COPY command at line 880 performs a screen dump to printer before the LPRINT statements at lines 890–910, meaning the printed output shows a screen snapshot followed by the text move log.

Content

Appears On

Explore a first-person 3D maze, watch an audio oscilloscope in real time, draw pixel art with ten drawing modes, or generate random surreal poetry — ISTUG Library 5 is a sprawling collection that covers every corner of the TS 2068 experience.

Related Products

Related Articles

Related Content

Image Gallery

Source Code

   10 REM "MEN MAN" by J.G.DuPuy 11/23/84
   20 GO TO 1110
   30 REM DRAW PEGS
   40 FOR B=0 TO 3: PLOT PL+B,128: DRAW 0,42: NEXT B: RETURN 
   50 REM DRAW DISCS
   60 LET PL=-34
   70 FOR n=SGN PI TO 5: PRINT AT N,1,,: NEXT n
   80 FOR N=SGN PI TO 3: LET PL=PL+80: GO SUB 40
   90 FOR O=SGN PI TO 5
  100 IF P(N,O)=0 THEN GO TO 130
  110 PRINT AT O,1+((N-1)*10); INK (6-P(N,O));D$(P(N,O))
  120 BEEP .05,P(N,O)+10
  130 NEXT O: NEXT n: RETURN 
  140 PRINT AT 16,10; FLASH 1;" STOP THE TAPE!": BEEP .5,0: BEEP .5,10: INPUT "Do you want intructions?(Y/N)"; LINE z$: IF z$="y" THEN GO SUB 1010
  150 PRINT AT 16,10;"                "
  160 INPUT "Enter:J-Joy stick,K-keys(1-3)"; LINE z$: LET st=0: IF z$="j" THEN LET st=1: GO SUB 1080
  170 PLOT 0,22: DRAW 255,0: DRAW 0,52: DRAW -255,0: DRAW 0,-52
  180 PRINT AT 14,17;"BEST SCORE:";hs
  190 IF st THEN PRINT AT 18,6;"LEFT=1, UP=2, RIGHT=3"
  200 REM MAIN PROGRAM
  210 LET M=M+1
  220 PRINT AT 16,1,,
  230 PRINT AT 14,1;"MOVE:";M
  240 IF m>100 THEN PRINT AT 16,1;"You have gone over you limit!": BEEP 1,-10: PAUSE 30: GO TO 950
  250 PRINT AT 16,1;"FROM?(1-3):";
  260 IF st THEN GO TO 690
  270 LET A$=INKEY$: IF A$="x" THEN GO TO 950
  280 IF A$>"3" OR A$<"1" THEN GO TO 270
  290 IF INKEY$<>"" THEN GO TO 290
  300 PRINT A$;: BEEP .1,27
  310 IF st AND |(1,1) THEN GO TO 310
  320 LET D=VAL A$: GO SUB 550
  330 IF Z THEN PRINT AT 16,1;"NOTHING THERE TO TAKE!": BEEP .5,-20: PAUSE 30: GO TO 220
  340 PRINT " / TO?(1-3):";
  350 IF st THEN GO TO 740
  360 LET B$=INKEY$: IF B$="x" THEN GO TO 950
  370 IF B$>"3" OR B$<"1" THEN GO TO 360
  380 IF INKEY$<>"" THEN GO TO 380
  390 PRINT B$: BEEP .1,30
  400 IF b$=a$ THEN PRINT AT 16,1;"A RATHER STUPID MOVE!!!   ": BEEP .5,-20: PAUSE 30: GO TO 220
  410 LET E=VAL B$: GO TO 620
  420 REM STORE MOVES
  430 LET M$=M$+A$+"-"+B$+":"
  440 REM UPDATE POSITION ARRAY
  450 LET P(D,I)=0: IF N=1 THEN LET P(E,N)=FR: GO SUB 60: GO TO 480
  460 LET P(E,N-1)=FR: GO SUB 60
  470 REM CHECK PEG#3 FOR WIN
  480 LET S=0
  490 FOR G=SGN PI TO 5
  500 IF P(3,G)=G THEN LET S=S+1
  510 NEXT G
  520 IF S=5 THEN GO TO 800
  530 GO TO 210
  540 REM "FROM"SETUP&ERROR CHECK
  550 DIM F(5)
  560 FOR I=SGN PI TO 5
  570 IF P(D,I)=0 THEN GO TO 590
  580 LET F(I)=P(D,I): LET Z=0: LET FR=F(I): RETURN 
  590 NEXT I
  600 LET Z=1: RETURN 
  610 REM "TO"SETUP & ERROR CHECK
  620 DIM T(5)
  630 FOR N=SGN PI TO 5
  640 IF P(E,N)=0 THEN GO TO 670
  650 LET T(N)=P(E,N): IF T(N)<FR THEN PRINT AT 16,1;"YOUR DISC IS TOO BIG! GO AGAIN": BEEP .5,-20: PAUSE 30: GO TO 210
  660 IF T(N)>0 THEN GO TO 430
  670 NEXT N
  680 GO TO 430
  690 IF |(1,1)=4 THEN LET a$="1": GO TO 290
  700 IF |(1,1)=1 THEN LET a$="2": GO TO 290
  710 IF |(1,1)=8 THEN LET a$="3": GO TO 290
  720 IF INKEY$="x" THEN GO TO 950
  730 GO TO 690
  740 IF |(1,1)=4 THEN LET b$="1": GO TO 380
  750 IF |(1,1)=1 THEN LET b$="2": GO TO 380
  760 IF |(1,1)=8 THEN LET b$="3": GO TO 380
  770 IF INKEY$="x" THEN GO TO 950
  780 GO TO 740
  790 REM WIN ROUTINE
  800 PLOT OVER 1;0,22: DRAW OVER 1;255,0: PLOT OVER 0;0,24: DRAW 0,-20: DRAW 255,0: DRAW 0,20: PRINT AT 18,1;"GOOD GOING! YOU GOT IT!",AT 19,1;"IT TOOK YOU : ";M-1;" MOVES.     "
  810 IF m-1<hs THEN LET hs=m-1: PRINT AT 14,17;"BEST SCORE:";hs;" "
  820 FOR N=SGN PI TO 20: BEEP .1,N+20: NEXT N
  830 GO SUB 860
  840 GO TO 990
  850 REM PRINT STORED MOVES
  860 INPUT "Want you moves printed?(Y/N):"; LINE z$: IF z$<>"y" THEN RETURN 
  870 INPUT "Turn printer on, press ENT "; LINE z$
  880 COPY 
  890 LPRINT "NUMBER of MOVES: ";M-1
  900 LPRINT '
  910 LPRINT M$
  920 LPRINT '
  930 RETURN 
  940 REM LOSE ROUTINE
  950 PRINT AT 20,1;"I guess I wiped your mind!!";AT 21,1;"Ha! Ha! Ha!"
  960 FOR n=SGN PI TO 20: BEEP .1,0-n: NEXT n
  970 GO SUB 860
  980 REM RESTART GAME
  990 INPUT "Want to play again?(Y/N):";z$: IF z$<>"n" THEN GO TO 30
 1000 STOP 
 1010 REM INSTRUCTIONS
 1020 PRINT AT 16,0,,
 1030 PRINT AT 12,0;"The object of the game is to    move all 5 of the discs one at atime so that you end up with all5 at 3 in the same order as 1 isnow in the least number of movesbut You can't put a larger disc on top of a smaller disc! Enter an ""X"" if you want to quit."
 1040 PRINT "BEWARE, You only have 100 moves!"
 1050 INPUT "Press enter to start.  "; LINE z$
 1060 FOR x=12 TO 20
 1070 PRINT AT x,0,,: NEXT x: RETURN 
 1080 PRINT AT 12,0;"You have elected to use the     JOY STICK. To move, LEFT=1, UP=2and RIGHT=3. X key to stop."
 1090 INPUT "Press enter to start   ";z$: FOR v=12 TO 16: PRINT AT v,0,,: NEXT v: RETURN 
 1100 REM INITIALIZE
 1110 POKE 23609,25
 1120 LET hs=100
 1130 BORDER 1: PAPER 7: LET m=0: INK m: BRIGHT 1: CLS 
 1140 FOR n=m TO 7: READ a: POKE USR "\a"+n,a: NEXT n
 1150 DATA 170,85,170,85,170,85,170,85
 1160 CLS : DIM p(PI,5): LET m$="": DIM d$(5,10)
 1170 LET d$(1)="\  \  \  \  \::\::"
 1180 LET d$(2)="\  \  \  \::\::\::\::"
 1190 LET d$(PI)="\  \  \::\::\::\::\::\::"
 1200 LET d$(4)="\  \::\::\::\::\::\::\::\::"
 1210 LET d$(5)="\::\::\::\::\::\::\::\::\::\::"
 1220 FOR N=SGN PI TO 5: LET P(1,N)=N: NEXT N
 1230 REM SET UP SCREEN
 1240 PRINT AT 6,m;"\a\a\a\a\a1\a\a\a\a\a\a\a\a\a2\a\a\a\a\a\a\a\a\a3\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a"
 1250 PRINT ' INK m;"     M E N T A L  M A N I A"
 1260 PLOT m,87: DRAW 255,m: DRAW m,88: DRAW -255,m: DRAW m,-88
 1270 GO SUB 60: GO TO 140
 1280 CLEAR : SAVE "MEN MAN" LINE 10

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top