! robotfindskitten ! A Zen Simulation ! Release 8 / Serial number 220120 / Inform v6.35 ! ! [-] |\_/| http://www.robotfindskitten.org ! (+)=C |o o|__ Leonard Richardson (C) 1997, 2000 ! | | --*--__\ David Griffith (C) 2002-2022 (Inform Edition) ! OOO C_C(____) ! ! ! This Zen simulation is based on the C version v1600003.248b ! by Leonard Richardson (C) 1997, 2000. ! Written originally for the Nerth Pork robotfindskitten contest. ! Reimplemented in Inform by David Griffith (C) 2002. ! ! Lots more information on robotfindskitten is available at ! http://www.robotfindskitten.org. ! ! This rendition of robotfindskitten is distributed under the Artistic ! License 2.0. See the file LICENSE in the robotfindskitten repository ! or https://opensource.org/licenses/Artistic-2.0 for more information. ! ! In this game, you are Robot (#). Your job is to find Kitten. This ! task is complicated by the existance of various things which are not ! kitten. Robot must touch items to determine if they are Kitten or ! not. Move Robot with the cursor keys, the numeric keypad, or ! using the vi/rogue movement keys. The game ends when robotfindskitten. ! Alternatively, you may end the game by hitting the Esc or Q keys. ! ! Notes: ! 1) More than half of the code is taken up by non kitten items ! (NKIs). When I compiled the code with just five messages and ! no debugging code, the resulting binary was less than 10k bytes. ! ! 2) If it wasn't already abundantly obvious, this program won't ! compile to Glulx because of copious use of Z-machine assembly ! instructions. ! ! 3) Compiling for V5 or higher is required due to "style" calls. ! Is there a reason why someone would want to compile this for V4 ! or previous? !Switches xv5s; Switches "d2~S"; Constant Nonkitten_Default 20; ! Maxmimum possible number of non-kitten items on the playfield at once. ! For whatever reason, this cannot be set dynamically. ! Constant Nonkitten_Max 589; Release 8; Serial "220120"; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Constant Story "robotfindskitten"; Constant Headline "^A Zen Simulation^"; ! NKIs are generated with nki2inf.pl and put into nki.inf Include "nki.inf"; Constant Anim_Meet 10; ! Number of spaces from the left where ! Robot and Kitten meet during animation. Global Height = 0; ! These are set at runtime. Global Width = 0; Global Back_def = 2; ! Black. Global Fore_def = 9; ! White. Global TopBar = 5; ! Lines from the top. Global player_x = 0; ! Keeping track of where the player was Global player_y = 0; ! 1 move ago allows us to keep the Global player_x_last = 0; ! player from walking through obstacles. Global player_y_last = 0; Global kitten_x = 0; Global kitten_y = 0; Global kitten_char = 0; Global kitten_color = 0; Global last_message = ""; ! Always show the last-encountered message. Global nonkitten_count; Array nonkitten_x --> Nonkitten_Max; Array nonkitten_y --> Nonkitten_Max; Array nonkitten_color --> Nonkitten_Max; Array nonkitten_char --> Nonkitten_Max; Array nonkitten_msg --> Nonkitten_Max; Global already_msg_count = 0; Global already_count = 0; Array already_x --> Nonkitten_Max + 2; Array already_y --> Nonkitten_Max + 2; Array already_msg --> Nonkitten_Max; ! If a key is held down while the found_kitten animation is playing, ! (0-->1) & $03ff gets corrupted. Seems like it might be a bug ! somewhere in Unix Frotz. ! Global Real_Release = 0; [ Main key; @set_colour Fore_def Back_def; if (MESSAGE_NUM < Nonkitten_Default) { nonkitten_count = MESSAGE_NUM; } else { nonkitten_count = Nonkitten_Default; } Real_Release = (0-->1)&$03ff; Width = $21->0; Height = $20->0; main_menu(); while (true) { key = getkey(); switch (key) { 'F': already_count = 0; init_nonkittens(); init_kitten(); init_robot(); while (findkitten()) ; 'D': set_nonkitten_count(); 'I': print_instructions(); 'A': print_about(); 'T': print_thoughts(); } if (key == 'Q' || key == $1b) ! $1b == ESC break; main_menu(); } quit; ]; [ main_menu psycho; ! There's a 1:50 chance that the kitten in the title screen ! will have a "psycho" appearance. ! psycho = random(50); if (psycho == 1) psycho = true; else psycho = false; @erase_window $ffff; @split_window 11; @set_window 1; Banner(); draw_horiz(TopBar); draw_big_robot(3, 7); if (psycho) draw_big_kitten_psycho(14, 7); else draw_big_kitten(15, 7); @set_cursor 7 30; print "http://www.robotfindskitten.org"; @set_cursor 8 30; print "Leonard Richardson (C) 1997, 2000"; @set_cursor 9 30; print "David Griffith (C) 2002-2022 (Inform Edition)"; @set_cursor 10 30; print " ", MESSAGE_NUM, " different nonkittens!"; @set_window 0; print " F) Find Kitten^", " D) Difficulty (", nonkitten_count, ")^", " I) Instructions^", " T) Thoughts^", " A) About^", " Q) Quit^", "^> "; ]; ! Copied from module/verblibm.h of the Inform 6.21.3 standard library. ! [ Banner i; if (Story ~= 0) { style bold; print (string) Story; style roman; } if (Headline ~= 0) { print (string) Headline; } print "Release ", Real_Release, " / Serial number "; for (i=18:i<24:i++) print (char) 0->i; print " / Inform v"; inversion; print ""; new_line; ]; Constant INBUFSIZE 80; Array inbuf -> INBUFSIZE; [ set_nonkitten_count maxnum val; while (true) { @erase_window $ffff; @split_window 5; @set_window 1; Banner(); draw_horiz(TopBar); @set_window 0; if (MESSAGE_NUM < Nonkitten_Max) { maxnum = MESSAGE_NUM; } else { maxnum = Nonkitten_Max; } print "^Please enter the number of nonkittens you wish to search through.^(1 to ", maxnum, " only)^^> "; while (true) { val = get_number(1, maxnum, nonkitten_count); if (val == -1) { break; } else { nonkitten_count = val; return; } } } ]; [ get_number min max init inbufvar ix cx len val; while (true) { inbuf->0 = (INBUFSIZE-3); inbuf->1 = 0; inbufvar = inbuf; ix = 0; @aread inbufvar ix; new_line; len = inbuf->1; cx = 0; while (cx < len && inbuf->(2+cx) == ' ') cx++; if (cx < len && inbuf->(2+cx) == '.') break; ! If user just hits return, use what we have already. if (len == 0) return init; if (cx == len || inbuf->(2+cx) < '0' || inbuf->(2+cx) > '9') { print "Please enter a value from ", min, " to ", max, ", or Enter by itself to exit.^ [Press any key to continue.] "; getkey(); return -1; } val = 0; while (cx < len && inbuf->(2+cx) >= '0' && inbuf->(2+cx) <= '9') { val = val * 10 + (inbuf->(2+cx) - '0'); cx++; } if (val < min || val > max) { print "Please enter a value from ", min, " to ", max, ", or Enter by itself to exit.^ [Press any key to continue.] "; getkey(); return -1; } else break; } return val; ]; [ print_about; @erase_window $ffff; @split_window TopBar; @set_window 1; Banner(); draw_horiz(TopBar); @set_window 0; print "^ This Zen simulation is based on the C version v1600003.248b^ by Leonard Richardson (C) 1997, 2000.^ Written originally for the Nerth Pork robotfindskitten contest.^ Reimplemented in Inform by David Griffith (C) 2002.^ ^ This code is distributed according to the Artistic License 2.0. See https://opensource.org/licenses/Artistic-2.0 for more information. I, David Griffith, retain copyright on this program except for the NKIs imported from the master (aka POSIX) port.^ ^ Lots more information on robotfindskitten is available at http://www.robotfindskitten.org.^ ^ To submit new NKI's, please go to the above URL.^ ^ ^ Release History:^ ^ Release 1 / Serial number 0211xx to 021214 or so^ Initial private release. Limited distribution for beta testing and debugging purposes.^ ^ Release 2 / Serial Number 021216^ First public release.^ ^ Release 3 / Serial Number 021221^ Bugfix release.^ - Movement keys 'J' and 'K' were swapped by mistake. Fixed.^ - Special PalmOS movement key support added.^ - More NKIs added (401 total).^ ^ Release 4 / Serial Number 030131^ Light overhaul release.^ - Now an official port of robotfindskitten.^ - Typos in NKIs fixed.^ - Fixed diagonal collision-detection strangeness.^ - Added color support.^ - Added an easter egg. Can you find it?^ - Removed PalmOS movement key support (superfluous and ugly).^ - Removed playfield resizing code (superfluous and ugly).^ - It's ~robotfindskitten~, not ~Robot Finds Kitten~.^ - Merged in new NKIs from the new POSIX release of robotfindskitten.^ - More NKIs added (561 total).^ ^ Release 5 / Serial Number 030524^ Even more NKIs release.^ - Idiotic typos fixed.^ - More NKIs added (602 total).^ ^ Release 6 / Serial Number 031116^ Challenge release.^ - More NKIs added (764 total).^ - Increased maximum difficulty to 589.^ - Lots more comments in the source code.^ - Assorted cleanups in the source code.^ ^ Release 7 / Serial Number 130320^ Modular release.^ - Synchronized NKIs and removed redundancies with the POSIX port.^ - NKIs now generated from an external file using nki2inf.pl.^ - NKIs reduced to 723 because of redundancies and recommended deletions.^ ^ Release 8 / Serial Number 220120^ - Twenty Year Anniversary release.^ - Fixed a problem that crashed some interpreters when robotfindskitten.^ - Fixed a potential problem of wrongly determining screen size.^ - Now distributed under the Artistic License 2.0.^ ^ ^ Known Bugs:^ ^ 1) I still don't know why already_seen_xy() occasionally causes Robot to get placed on top of another object when a game is started. Fortunately this seems to happen only very rarely and typically only if the difficulty is set to more than 200. This bug also seems to very occasionally put Kitten underneath an NKI.^ ^ 2) Under earlier versions of Windows Frotz, Robot used to appear as a solid block. This was because of a bug in Windows Frotz which incorrectly makes the cursor opaque. The cursor is now moved off to the upper-right corner so that the game looks okay on terminals that use something other than reverse for the cursor. I still can't figure out how to make Inform hide the cursor completely. At least on xterm and NetBSD's console, @@64set_cursor -1 doesn't work.^ ^ 3) Under Windows Frotz, an annoying [MORE] prompt might appear at the main menu. This is another bug in Windows Frotz which causes the interpreter to follow Windows' suggestion that something less than 24 or 25 lines is okay.^ ^ [Press any key to continue.] "; getkey(); ]; [ print_instructions; @erase_window $ffff; @split_window TopBar; @set_window 1; Banner(); draw_horiz(TopBar); @set_window 0; print "^ In this game, you are Robot ( "; style reverse; print "#"; style roman; print " ). Your job is to find Kitten. This task is complicated by the existance of various things which are not Kitten. Robot must touch items to determine if they are Kitten or not. Move Robot with the cursor keys, the numeric keypad (make sure numlock is on), or using the vi/rogue/nethack movement keys. The game ends when robotfindskitten. Alternatively, you may end the game by hitting the Esc or Q keys.^ ^ [Press any key to continue.] "; getkey(); ]; [ print_thoughts; @erase_window $ffff; @split_window TopBar; @set_window 1; Banner(); draw_horiz(TopBar); @set_window 0; print "^ A Final Thought.^ ^ Day and night I feverishly worked upon the machine, creating both a soul which could desire its goal, and a body with which it could realize it. Many who saw my creation called it an abomination, and denied me grant money. But they could not dissuade me from my impossible task. It was a spectre that tormented me always, a ghost I had to give a form and a life, lest it consume me from the inside. And when at last my task was done, when the grey box on wheels was complete and when it, as well as I, knew what had to be done, I felt deep sympathy for the machine. For I had not destroyed the phantom, but merely exorcized it into another body. The robot knew not why this task had to be performed, for I could not imbue it with knowledge I did not myself posess. And at the same time, I felt a sweeping sense of relief sweep over me, that somehow, the dream that had driven me for my entire life had come one step closer to fruition.^ ^ ~Gort, Klaatu Verada Nikto~^ ^ As I vocally activated the robot, I realized that it was following my instructions, but not out of any desire to obey me. Had I remained silent, it would have performed exactly the same operations. We were two beings controlled by the same force now. And yet, seeking vainly to hold some illusion of control over the machine I thought I had created, I gave my final command.^ ^ ~GO!~ I told the box as it began to roll out of my workshop into the frozen desert beyond. ~FIND KITTEN!~^ ^ -- The Book of Found Kittens, pages 43-4, author unknown.^ ^ [Press any key to continue.] "; getkey(); ]; [ draw_big_robot x y; if (x == 0) x = 1; if (y == 0) y = 1; @set_cursor y x; @set_colour 6 Back_def; print "["; @set_colour 4 Back_def; print "-"; @set_colour 6 Back_def; print "]"; y = y+1; @set_cursor y x; @set_colour 6 Back_def; print "("; @set_colour 3 Back_def; print "+"; @set_colour 6 Back_def; print ")"; @set_colour 8 Back_def; print "=C"; y = y+1; @set_cursor y x; @set_colour 6 Back_def; print "| |"; y = y+1; @set_cursor y x; @set_colour 8 Back_def; print "OOO"; @set_colour Fore_def Back_def; ]; [ draw_big_kitten x y; if (x == 0) x = 1; if (y == 0) y = 1; @set_cursor y x; @set_colour 5 Back_def; print "|", (char) 92, "_/|"; y++; @set_cursor y x; print "|"; @set_colour 4 Back_def; print "o o"; @set_colour 5 Back_def; print "|__"; y++; @set_cursor y x; @set_colour 9 Back_def; print "--"; @set_colour 3 Back_def; print "*"; @set_colour 9 Back_def; print "--"; @set_colour 5 Back_def; print "__", (char) 92; y++; @set_cursor y x; print "C_C(____)"; @set_colour Fore_def Back_def; ]; [ draw_big_kitten_psycho x y; if (x == 0) x = 1; if (y == 0) y = 1; @set_cursor y x; @set_colour 5 Back_def; print " |", (char) 92, "_/|"; y++; @set_cursor y x; @set_colour 4 Back_def; print "(|) (|)"; @set_colour 5 Back_def; print "_"; y++; @set_cursor y x; @set_colour 9 Back_def; print " --"; @set_colour 3 Back_def; print "O"; @set_colour 9 Back_def; print "--"; @set_colour 5 Back_def; print "__", (char) 92; y++; @set_cursor y x; print " 3_3(____)"; @set_colour Fore_def Back_def; ]; ! Something gets messed up if I make this local to findkitten() ! When going right or left, then up or down to hit the Kitten, the ! animation gets reversed. ! Global last_right = false; [ findkitten key i; @erase_window $ffff; @split_window TopBar; @set_window 1; @set_cursor 1 1; Banner(); print (string) last_message; draw_horiz(TopBar); draw_object(kitten_x, kitten_y, kitten_char, kitten_color); draw_nonkittens(); style reverse; draw_object(player_x, player_y, '#'); style roman; @set_cursor 1 Width; ! Get movement key ! key = getkey(); ! Move Robot ! player_x_last = player_x; player_y_last = player_y; switch (key) { 'Q', $1b: rfalse; ! exit game ($1b == Esc) '8', 'K', 129: player_y--; ! up '2', 'J', 130: player_y++; ! down '4', 'H', 131: player_x--; ! left last_right = false; '6', 'L', 132: player_x++; ! right last_right = true; '7', 'Y': player_y--; player_x--; ! up-left last_right = false; '9', 'U': player_y--; player_x++; ! up-right last_right = true; '1', 'B': player_y++; player_x--; ! down-left last_right = false; '3', 'N': player_y++; player_x++; ! down-right last_right = true; } ! Keep Robot from falling off edges of playfield. ! if (player_y == TopBar || player_y > Height) { player_y = player_y_last; } if (player_x < 1 || player_x > Width) { player_x = player_x_last; } ! Detect and handle collisions. ! if (player_x == kitten_x && player_y == kitten_y) { animate_kitten(key, last_right); getkey(); rfalse; } for (i = 0: i < nonkitten_count: i++) { if (player_x == nonkitten_x-->i && player_y == nonkitten_y-->i) { @set_cursor 1 1; last_message = lookup_msg(nonkitten_msg-->i); player_x = player_x_last; player_y = player_y_last; } } rtrue; ]; [ animate_kitten key my_last_right i j junk robot_x anim_finished; switch (key) { '8', 'J', 129: player_y++; '2', 'K', 130: player_y--; '4', 'H', 131: player_x++; '6', 'L', 132: player_x--; '7', 'Y': player_y++; player_x++; '9', 'U': player_y++; player_x--; '1', 'B': player_y--; player_x++; '3', 'N': player_y--; player_x--; } anim_finished = false; for (i = 4: i >= 0: i--) { @erase_window $ffff; @split_window TopBar; @set_window 1; @set_cursor 1 1; Banner(); draw_horiz(TopBar); if (i > 0) { if (my_last_right) { robot_x = Anim_Meet - i; style reverse; draw_object(robot_x, TopBar - 1, '#'); style roman; draw_object(Anim_Meet - 1 + i, TopBar - 1, kitten_char, kitten_color); } else { robot_x = Anim_Meet - 1 + i; style reverse; draw_object(robot_x, TopBar - 1, '#'); style roman; draw_object(Anim_Meet - i, TopBar - 1, kitten_char, kitten_color); } } else { j = TopBar - 1; @set_cursor j 1; print "You found Kitten! Way to go, Robot!"; anim_finished = true; } draw_object(kitten_x, kitten_y, kitten_char, kitten_color); style reverse; draw_object(player_x, player_y, '#'); style roman; draw_nonkittens(); if (anim_finished == false) { j = TopBar - 1; @set_cursor 1 Width; @read_char 1 10 pause -> junk; @nop; ! This is for padding. } else { style reverse; draw_object(player_x, player_y, '#'); style roman; @set_cursor 1 Width; } } ]; [ already_seen_xy x y i; for (i = 0: i < already_count: i++) { if (already_x-->already_count == x && already_y-->already_count ==y) { rtrue; } } already_x-->already_count = x; already_y-->already_count = y; already_count++; rfalse; ]; [ pause; rtrue; ]; [ init_kitten; kitten_x = get_random_x(); kitten_y = get_random_y(); kitten_color = get_random_color(); while (already_seen_xy(kitten_x, kitten_y) == true) { kitten_x = get_random_x(); kitten_y = get_random_y(); } kitten_char = get_random_char(); ]; [ init_robot; player_x = get_random_x(); player_y = get_random_y(); while (already_seen_xy(player_x, player_y) == true) { player_x = get_random_x(); player_y = get_random_y(); } ]; [ init_nonkittens i; already_msg_count = 0; last_message = ""; for (i = 0: i < nonkitten_count: i++) { nonkitten_x-->i = get_random_x(); nonkitten_y-->i = get_random_y(); nonkitten_color-->i = get_random_color(); while (already_seen_xy(nonkitten_x-->i, nonkitten_y-->i) == true) { nonkitten_x-->i = get_random_x(); nonkitten_y-->i = get_random_y(); } nonkitten_char-->i = get_random_char(); nonkitten_msg-->i = get_random_msg(); } ]; [ draw_nonkittens i; for (i = 0: i < nonkitten_count: i++) { draw_object(nonkitten_x-->i, nonkitten_y-->i, nonkitten_char-->i, nonkitten_color-->i); } ]; [ draw_object x y character fore back; @set_cursor y x; if (fore == "") fore = Back_def; if (back == "") back = Back_def; @set_colour fore Back_def; if (character) print (char) character; @set_colour Fore_def Back_def; ]; [ draw_horiz row i; @set_cursor row 1; for (i = 0 : i < Width : i++) print (char) '-'; ]; [ getkey x; @read_char 1 -> x; if (x >= 'a' && x <= 'z') x = x - ('a' - 'A'); return x; ]; [ get_random_char num; num = random(93); num = num + 33; while (num == 35) { ! avoid choosing '#' num = random(93); num = num + 33; } return num; ]; [ get_random_msg num; num = random(MESSAGE_NUM); while (is_duplicate_msg(num) == true || num > MESSAGE_NUM || num < 1) { num = random(MESSAGE_NUM); } return num; ]; [ get_random_color num; num = random(7) + 2; ! 0 and 1 are default color and current color ! and we want to avoid picking the default color explicitly while (num == $2c-->0) { num = random(7) + 2; } return num; ]; [ is_duplicate_msg num i; for (i = 0: i < already_msg_count: i++) { if (already_msg-->i==num) { rtrue; } } already_msg-->already_msg_count = num; already_msg_count++; rfalse; ]; [ get_random_x; ! Maybe this will need to do something more in the future. return random(Width); ]; [ get_random_y num; ! Make sure we don't draw in the status bar. while (true) { num = random(Height); if (num > TopBar) return num; } ];