Bug

Descripción del contenido de la página

Conversión de Bug a varios lenguajes de programación.

Etiquetas:

Este programa ha sido convertido a 15 lenguajes de programación.

Original

Origin of bug.bas:

By Brian Leibowitz, 1978.

Creative Computing's BASIC Games.

- http://vintage-basic.net/games.html
- http://vintage-basic.net/bcg/bug.bas
- https://www.atariarchives.org/basicgames/showpage.php?page=30
- http://www.retroarchive.org/cpm/games/ccgames.zip

10 PRINT TAB(34);"BUG"
20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
30 PRINT:PRINT:PRINT
40 REM
50 A=0: B=0: H=0: L=0: N=0: P=0: Q=0: R=0: S=0: T=0: U=0: V=0: Y=0
60 PRINT "THE GAME BUG"
70 PRINT "I HOPE YOU ENJOY THIS GAME."
80 PRINT
90 PRINT "DO YOU WANT INSTRUCTIONS";
100 INPUT Z$
110 IF Z$="NO" THEN 300
120 PRINT "THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH"
130 PRINT "MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY."
140 PRINT "I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU"
150 PRINT "WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART."
160 PRINT "IF YOU CAN GET THE PART I WILL GIVE IT TO YOU."
170 PRINT "THE SAME WILL HAPPEN ON MY TURN."
180 PRINT "IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE"
190 PRINT "OPTION OF SEEING THE PICTURES OF THE BUGS."
200 PRINT "THE NUMBERS STAND FOR PARTS AS FOLLOWS:"
210 PRINT "NUMBER","PART","NUMBER OF PART NEEDED"
220 PRINT "1","BODY","1"
230 PRINT "2","NECK","1"
240 PRINT "3","HEAD","1"
250 PRINT "4","FEELERS","2"
260 PRINT "5","TAIL","1"
270 PRINT "6","LEGS","6"
280 PRINT
290 PRINT
300 IF Y>0 THEN 2480
310 Z=INT(6*RND(1)+1)
320 C=1
330 PRINT "YOU ROLLED A";Z
340 ON Z GOTO 350,430,540,650,760,870
350 PRINT "1=BODY"
360 IF B=1 THEN 410
370 PRINT "YOU NOW HAVE A BODY."
380 B=1
390 C=0
400 GOTO 970
410 PRINT "YOU DO NOT NEED A BODY."
420 GOTO 970
430 PRINT "2=NECK"
440 IF N=1 THEN 500
450 IF B=0 THEN 520
460 PRINT "YOU NOW HAVE A NECK."
470 N=1
480 C=0
490 GOTO 970
500 PRINT "YOU DO NOT NEED A NECK."
510 GOTO 970
520 PRINT "YOU DO NOT HAVE A BODY."
530 GOTO 970
540 PRINT "3=HEAD"
550 IF N=0 THEN 610
560 IF H=1 THEN 630
570 PRINT "YOU NEEDED A HEAD."
580 H=1
590 C=0
600 GOTO 970
610 PRINT "YOU DO NOT HAVE A NECK."
620 GOTO 970
630 PRINT "YOU HAVE A HEAD."
640 GOTO 970
650 PRINT "4=FEELERS"
660 IF H=0 THEN 740
670 IF A=2 THEN 720
680 PRINT "I NOW GIVE YOU A FEELER."
690 A=A+1
700 C=0
710 GOTO 970
720 PRINT "YOU HAVE TWO FEELERS ALREADY."
730 GOTO 970
740 PRINT "YOU DO NOT HAVE A HEAD."
750 GOTO 970
760 PRINT "5=TAIL"
770 IF B=0 THEN 830
780 IF T=1 THEN 850
790 PRINT "I NOW GIVE YOU A TAIL."
800 T=T+1
810 C=0
820 GOTO 970
830 PRINT "YOU DO NOT HAVE A BODY."
840 GOTO 970
850 PRINT "YOU ALREADY HAVE A TAIL."
860 GOTO 970
870 PRINT "6=LEG"
880 IF L=6 THEN 940
890 IF B=0 THEN 960
900 L=L+1
910 C=0
920 PRINT "YOU NOW HAVE";L;"LEGS."
930 GOTO 970
940 PRINT "YOU HAVE 6 FEET ALREADY."
950 GOTO 970
960 PRINT "YOU DO NOT HAVE A BODY."
970 X=INT(6*RND(1)+1)
971 PRINT
975 FOR DELAY=1 TO 2000:NEXT DELAY
980 PRINT "I ROLLED A";X
990 ON X GOTO 1000,1080,1190,1300,1410,1520
1000 PRINT "1=BODY"
1010 IF P=1 THEN 1060
1020 PRINT "I NOW HAVE A BODY."
1030 C=0
1040 P=1
1050 GOTO 1630
1060 PRINT "I DO NOT NEED A BODY."
1070 GOTO 1630
1080 PRINT "2=NECK"
1090 IF Q=1 THEN 1150
1100 IF P=0 THEN 1170
1110 PRINT "I NOW HAVE A NECK."
1120 Q=1
1130 C=0
1140 GOTO 1630
1150 PRINT "I DO NOT NEED A NECK."
1160 GOTO 1630
1170 PRINT "I DO NOT HAVE A BODY."
1180 GOTO 1630
1190 PRINT "3=HEAD"
1200 IF Q=0 THEN 1260
1210 IF R=1 THEN 1280
1220 PRINT "I NEEDED A HEAD."
1230 R=1
1240 C=0
1250 GOTO 1630
1260 PRINT "I DO NOT HAVE A NECK."
1270 GOTO 1630
1280 PRINT "I DO NOT NEED A HEAD."
1290 GOTO 1630
1300 PRINT "4=FEELERS"
1310 IF R=0 THEN 1390
1320 IF S=2 THEN 1370
1330 PRINT "I GET A FEELER."
1340 S=S+1
1350 C=0
1360 GOTO 1630
1370 PRINT "I HAVE 2 FEELERS ALREADY."
1380 GOTO 1630
1390 PRINT "I DO NOT HAVE A HEAD."
1400 GOTO 1630
1410 PRINT "5=TAIL"
1420 IF P=0 THEN 1480
1430 IF U=1 THEN 1500
1440 PRINT "I NOW HAVE A TAIL."
1450 U=1
1460 C=0
1470 GOTO 1630
1480 PRINT "I DO NOT HAVE A BODY."
1490 GOTO 1630
1500 PRINT "I DO NOT NEED A TAIL."
1510 GOTO 1630
1520 PRINT "6=LEGS"
1530 IF V=6 THEN 1590
1540 IF P=0 THEN 1610
1550 V=V+1
1560 C=0
1570 PRINT "I NOW HAVE";V;"LEGS."
1580 GOTO 1630
1590 PRINT,"I HAVE 6 FEET."
1600 GOTO 1630
1610 PRINT "I DO NOT HAVE A BODY."
1620 GOTO 1630
1630 IF A=2 AND T=1 AND L=6 THEN 1650
1640 GOTO 1670
1650 PRINT "YOUR BUG IS FINISHED."
1660 Y=Y+1
1670 IF S=2 AND P=1 AND V=6 THEN 1690
1680 GOTO 1710
1690 PRINT "MY BUG IS FINISHED."
1700 Y=Y+2
1710 IF C=1 THEN 300
1720 PRINT "DO YOU WANT THE PICTURES";
1730 INPUT Z$
1740 IF Z$="NO" THEN 300
1750 PRINT "*****YOUR BUG*****"
1760 PRINT
1770 PRINT
1780 IF A=0 THEN 1860
1790 FOR Z=1 TO 4
1800 FOR X=1 TO A
1810 PRINT TAB(10);
1820 PRINT "A ";
1830 NEXT X
1840 PRINT
1850 NEXT Z
1860 IF H=0 THEN 1880
1870 GOSUB 2470
1880 IF N=0 THEN 1920
1890 FOR Z=1 TO 2
1900 PRINT "          N N"
1910 NEXT Z
1920 IF B=0 THEN 2000
1930 PRINT "     BBBBBBBBBBBB"
1940 FOR Z=1 TO 2
1950 PRINT "     B          B"
1960 NEXT Z
1970 IF T<>1 THEN 1990
1980 PRINT "TTTTTB          B"
1990 PRINT "     BBBBBBBBBBBB"
2000 IF L=0 THEN 2080
2010 FOR Z=1 TO 2
2020 PRINT TAB(5);
2030 FOR X=1 TO L
2040 PRINT " L";
2050 NEXT X
2060 PRINT
2070 NEXT Z
2080 FOR Z=1 TO 4
2090 PRINT
2100 NEXT Z
2110 PRINT "*****MY BUG*****"
2120 PRINT
2130 PRINT
2140 PRINT
2150 IF S=0 THEN 2230
2160 FOR Z=1 TO 4
2170 PRINT TAB(10);
2180 FOR X=1 TO S
2190 PRINT "F ";
2200 NEXT X
2210 PRINT
2220 NEXT Z
2230 IF R<>1 THEN 2250
2240 GOSUB 2470
2250 IF Q=0 THEN 2280
2260 PRINT "          N N"
2270 PRINT "          N N"
2280 IF P=0 THEN 2360
2290 PRINT "     BBBBBBBBBBBB"
2300 FOR Z=1 TO 2
2310 PRINT "     B          B"
2320 NEXT Z
2330 IF U<>1 THEN 2350
2340 PRINT "TTTTTB          B"
2350 PRINT "     BBBBBBBBBBBB"
2360 IF V=0 THEN 2450
2370 FOR Z=1 TO 2
2380 PRINT TAB(5);
2390 FOR X=1 TO V
2400 PRINT " L";
2410 NEXT X
2420 PRINT
2430 NEXT Z
2450 IF Y<>0 THEN 2540
2460 GOTO 300
2470 PRINT "        HHHHHHH"
2480 PRINT "        H     H"
2490 PRINT "        H O O H"
2500 PRINT "        H     H"
2510 PRINT "        H  V  H"
2520 PRINT "        HHHHHHH"
2530 RETURN
2540 PRINT "I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!"
2550 END

En Arturo

; Bug

; Original version in BASIC:
;   Brian Leibowitz, 1978.
;   Creative Computing's BASIC Games.
;   - http://vintage-basic.net/games.html
;   - http://vintage-basic.net/bcg/bug.bas
;   - https://www.atariarchives.org/basicgames/showpage.php?page=30
;   - http://www.retroarchive.org/cpm/games/ccgames.zip

; This version in Arturo:
;   Copyright (c) 2023, Marcos Cruz (programandala.net)
;   SPDX-License-Identifier: Fair
;
; Written in 2023-10.
;
; Last modified: 20251205T0045+0100.

bugRecord: dictionary [
    body: false
    neck: false
    head: false
    feelers: 0
    feelerType: ' '
    tail: false
    legs: 0
]

playerRecord: dictionary [
    pronoun: ""
    possessive: ""
    bug: bugRecord
]

; Players.
computer: new playerRecord
computer\bug: new bugRecord
human: new playerRecord
human\bug: new bugRecord

; Bug body parts.
partNumber: dictionary [
    body: 1
    neck: 2
    head: 3
    feeler: 4
    tail: 5
    leg: 6
]

partName: array [
    ""
    "body"
    "neck"
    "head"
    "feeler"
    "tail"
    "leg"
]

; Bug body attributes.
bodyHeight: 2
feelerLength: 4
legLength: 2
maxFeelers: 2
maxLegs: 6
neckLength: 2

; Move the cursor up by the given number of rows (defaults to 1), without
; changing the column position.
cursorUp: function [ rows ] [
    prints render "\e[|rows|A"
]

; Erase the current line, without moving the cursor position.
eraseLine: function [ ] [
    prints "\e[2K"
]

; Move the cursor to the previous row, without changing the column position,
; and erase its line.
erasePreviousLine: function [ ] [
    cursorUp 1
    eraseLine
]

; Clear the screen, display the credits and wait for a keypress.
printCredits: function [ ] [
    clear
    print "Bug\n"
    print "Original version in BASIC:"
    print "    Brian Leibowitz, 1978."
    print "    Creative computing (Morristown, New Jersey, USA), 1978.\n"
    print "This version in Arturo:"
    print "    Copyright (c) 2023, Marcos Cruz (programandala.net)"
    print "    SPDX-License-Identifier: Fair\n"
    input "Press Enter to read the instructions. "
]

instructions: {:
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
:}

; Capitalize the first character of `s` and convert the rest of `s` to
; lowercase.
toCapital: function [ s ] [
    capitalize lower s
]

; ; Print a table with the bug parts' description.
printPartsTable: function [ ] [

    columns: 3
    columnWidth: 8
    columnSeparation: 2

    ; Headers
    header: [ "Number" "Part" "Quantity" ]
    loop range 0 sub columns 1  'i [
        prints pad .right header\[ i ] add columnWidth columnSeparation
    ]
    print ""

    ; Rulers
    loop range 1 columns 'i [
        prints repeat "-" columnWidth
        if? less? i columns [ prints repeat " " columnSeparation ]
    ]
    print ""

    ; Data
    partQuantity: array .of: add size partNumber 1 0
    partQuantity\[ partNumber\body ]: 1
    partQuantity\[ partNumber\neck ]: 1
    partQuantity\[ partNumber\head ]: 1
    partQuantity\[ partNumber\feeler ]: 2
    partQuantity\[ partNumber\tail ]: 1
    partQuantity\[ partNumber\leg ]: 6
    loop range partNumber\body partNumber\leg 'part [
        prints pad .right to :string part add columnWidth columnSeparation
        prints pad .right toCapital partName\[part] add columnWidth columnSeparation
        print partQuantity\[ part ]
    ]

]

; Clear the screen, print the instructions and wait for a keypress.
printInstructions: function [ ] [
    clear
    print "Bug"
    print instructions
    printPartsTable
    input "\nPress Enter to start. "
]

; Print a bug head.
printHead: function [ ] [
    print "        HHHHHHH"
    print "        H     H"
    print "        H O O H"
    print "        H     H"
    print "        H  V  H"
    print "        HHHHHHH"
]

; Print the given bug.
printBug: function [ bug ] [
    if greater? bug\feelers 0 [
        loop range 0 sub feelerLength 1  'i [
            prints "        "
            loop range 0 sub bug\feelers 1  'j [
                prints render " |bug\feelerType|"
            ]
            print ""
        ]
    ]
    if bug\head [
        printHead
    ]
    if bug\neck [
        loop range 0 sub neckLength 1  'i [
            print "          N N"
        ]
    ]
    if bug\body [
        print "     BBBBBBBBBBBB"
        loop range 0 sub bodyHeight 1  'i [
            print "     B          B"
        ]
        if bug\tail [
            print "TTTTTB          B"
        ]
        print "     BBBBBBBBBBBB"
    ]
    if greater? bug\legs 0 [
        loop range 0 sub legLength 1  'i [
            prints "    "
            loop range 0 sub bug\legs 1  'j [
                prints " L"
            ]
            print ""
        ]
    ]
    print ""
]

; Return `true` if the given bug is finished; otherwise return `false`.
isFinished?: function [ bug ] [
    and?
        and?
            equal? bug\feelers maxFeelers
            bug\tail
        equal? bug\legs maxLegs
]

; Return a random number from 1 to 6 (inclusive).
dice: function [ ] [
    random 1 6
]

; Array to convert a number to its equilavent text.
asText: [ "no" "a" "two" "three" "four" "five" "six" ]

; Return the proper plural ending depending of the given number.
pluralEnding: function [ n ] [
    if? greater? n 1 [ "s" ] else [ "" ]
]

; Return a string containing the given number and noun in their proper form.
plural: function [ noun number ] [
    render "|asText\[number]| |noun||pluralEnding number|"
]

; Add the given part to the given player's bug.
addPart: function [ part player ] [
    changed: false
    case [ part ]
    when? [ equal? partNumber\body ] [
        if? player\bug\body [
            print render ", but |player\pronoun| already have a body."
        ] else [
            print render "; |player\pronoun| now have a body:"
            player\bug\body: true
            changed: true
        ]
    ]
    when? [ equal? partNumber\neck ] [
        if? player\bug\neck [
            print render ", but |player\pronoun| you already have a neck."
        ] else [
            if? not? player\bug\body [
                print render ", but |player\pronoun| need a body first."
            ] else [
                print render "; |player\pronoun| now have a neck:"
                player\bug\neck: true
                changed: true
            ]
        ]
    ]
    when? [ equal? partNumber\head ] [
        if? player\bug\head [
            print render ", but |player\pronoun| already have a head."
        ] else [
            if? not? player\bug\neck [
                print render ", but |player\pronoun| need a a neck first."
            ] else [
                print render "; |player\pronoun| now have a head:"
                player\bug\head: true
                changed: true
            ]
        ]
    ]
    when? [ equal? partNumber\feeler ] [
        if? equal? player\bug\feelers maxFeelers [
            print render ", but |player\pronoun| have two feelers already."
        ] else [
            if? not? player\bug\head [
                print render ", but |player\pronoun| need a head first."
            ] else [
                player\bug\feelers: player\bug\feelers + 1
                prints render "; |player\pronoun| now have "
                prints plural "feeler" player\bug\feelers
                print ":"
                changed: true
            ]
        ]
    ]
    when? [ equal? partNumber\tail ] [
        if? player\bug\tail [
            print render ", but |player\pronoun| already have a tail."
        ] else [
            if? not? player\bug\body [
                print render ", but |player\pronoun| need a body first."
            ] else [
                print render "; |player\pronoun| now have a tail:"
                player\bug\tail: true
                changed: true
            ]
        ]
    ]
    when? [ equal? partNumber\leg ] [
        if? equal? player\bug\legs maxLegs [
            print render ", but |player\pronoun| have |asText\[maxLegs]| feet already."
        ] else [
            if? not? player\bug\body [
                print render ", but |player\pronoun| need a body first."
            ] else [
                player\bug\legs: player\bug\legs + 1
                prints render "; |player\pronoun| now have "
                prints plural "leg" player\bug\legs
                print ":"
                changed: true
            ]
        ]
    ]
    else [ ]
    changed
]

; Ask the user to press the Enter key, wait for the input, then erase the
; prompt text.
prompt: function [ ] [
    input "Press Enter to roll the dice. "
    erasePreviousLine
]

; Play one turn for the given player, rolling the dice and updating his bug.
turn: function [ player ] [
    prompt
    number: dice
    prints render "|toCapital player\pronoun| rolled a |number| (|partName\[number]|)"
    if addPart number player [
        print ""
        printBug player\bug
    ]
]

; Return `true` if both bugs are finished, otherwise return `false`.
tie?: function [ ] [
    and? isFinished? human\bug isFinished? computer\bug
]

; Print a message about the winner.
printWinner: function [ ] [
    if? tie? [
        print "Both of our bugs are finished in the same number of turns!"
    ] else [
        if? isFinished? human\bug [ prints human\possessive ]
        else [ prints computer\possessive ]
        print " bug is finished."
    ]
]

; Return `true` if any bug is finished; otherwise return `false`.
gameOver?: function [ ] [
    or? isFinished? human\bug isFinished? computer\bug
]

; Execute the game loop.
play: function [ ] [
    clear
    until [
        turn human
        turn computer
    ] [ gameOver? ]
    printWinner
]

; Init the players' data before a new game.
init: function [ ] [
    human\pronoun: "you"
    human\possessive: "Your"
    human\bug\feelerType: 'A'
    computer\pronoun: "I"
    computer\possessive: "My"
    computer\bug\feelerType: 'F'
]

init
printCredits
printInstructions
play
print "I hope you enjoyed the game, play it again soon!!"

En C#

// Bug

// Original version in BASIC:
//  Brian Leibowitz, 1978.
//  Creative Computing's BASIC Games.
//  - http://vintage-basic.net/games.html
//  - http://vintage-basic.net/bcg/bug.bas
//  - https://www.atariarchives.org/basicgames/showpage.php?page=30
//  - http://www.retroarchive.org/cpm/games/ccgames.zip

// This version in C#:
//  Copyright (c) 2024, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written in 2024-12-20/21.
//
// Last modified: 20251205T1532+0100.

using System;

class BugGame
{
    class ABug
    {
        internal bool body = false;
        internal bool neck = false;
        internal bool head = false;
        internal int feelers = 0;
        internal char feelerType = ' ';
        internal bool tail = false;
        internal int legs = 0;
    }

    class Player
    {
        internal string pronoun = "";
        internal string possessive = "";
        internal ABug bug = new ABug();
    }

    static Player computer = new Player();
    static Player human = new Player();

    enum Part {body = 1, neck, head, feeler, tail, leg};
    static int parts = Enum.GetNames(typeof(Part)).Length;

    // Bug body attributes.
    //
    const int BODY_HEIGHT = 2;
    const int FEELER_LENGTH = 4;
    const int LEG_LENGTH = 2;
    const int MAX_FEELERS = 2;
    const int MAX_LEGS = 6;
    const int NECK_LENGTH = 2;

    static void PrintCredits()
    {
        Console.Clear();
        Console.WriteLine("Bug\n");
        Console.WriteLine("Original version in BASIC:");
        Console.WriteLine("    Brian Leibowitz, 1978.");
        Console.WriteLine("    Creative computing (Morristown, New Jersey, USA), 1978.\n");
        Console.WriteLine("This version in C#:");
        Console.WriteLine("    Copyright (c) 2024, Marcos Cruz (programandala.net)");
        Console.WriteLine("    SPDX-License-Identifier: Fair\n");
        Console.Write("Press Enter to read the instructions. ");
        Console.ReadKey();
    }

    // Return the given string with its first character in uppercase and the
    // rest in lowercase.
    //
    static string Capitalized(string str)
    {
        if (string.IsNullOrEmpty(str))
        {
            return string.Empty;
        }
        else
        {
            return char.ToUpper(str[0]) + str.Substring(1).ToLower();
        }
    }

    // Write the given string the given number of times.
    //
    static void WriteTimes(string s, int n)
    {
        for (int t = 0; t < n; t++)
        {
            Console.Write(s);
        }
    }

    // Print a table with the description of the bug parts.
    //
    static void PrintPartsTable()
    {
        const int COLUMNS = 3;
        const int COLUMN_WIDTH = 8;
        const int COLUMN_SEPARATION = 2;

        // Headers

        string[] header = {"Number", "Part", "Quantity"};
        for (int i = 0; i < COLUMNS; i++)
        {
            Console.Write(header[i].PadRight(COLUMN_WIDTH + COLUMN_SEPARATION));
        }
        Console.WriteLine();

        // Rulers

        for (int i = 0; i < COLUMNS; i++)
        {
            WriteTimes("-", COLUMN_WIDTH);
            if (i != COLUMNS - 1)
            {
                WriteTimes(" ", COLUMN_SEPARATION);
            }
        }
        Console.WriteLine();

        // Data

        int[] partQuantity = new int[parts + 1];

        partQuantity[(int) Part.body] = 1;
        partQuantity[(int) Part.neck] = 1;
        partQuantity[(int) Part.head] = 1;
        partQuantity[(int) Part.feeler] = 2;
        partQuantity[(int) Part.tail] = 1;
        partQuantity[(int) Part.leg] = 6;

        for (Part p = Part.body; (int) p < parts; p++)
        {
            Console.Write($"{(int) p}".PadRight(COLUMN_WIDTH + COLUMN_SEPARATION));
            Console.Write(Capitalized(Enum.GetName(typeof(Part), p).PadRight(COLUMN_WIDTH + COLUMN_SEPARATION)));
            Console.WriteLine(partQuantity[(int) p]);
        }
    }

    static void PrintInstructions()
    {
        Console.Clear();
        Console.WriteLine("Bug\n");
        Console.WriteLine("The object is to finish your bug before I finish mine. Each number");
        Console.WriteLine("stands for a part of the bug body.\n");
        Console.WriteLine("I will roll the die for you, tell you what I rolled for you, what the");
        Console.WriteLine("number stands for, and if you can get the part. If you can get the");
        Console.WriteLine("part I will give it to you. The same will happen on my turn.\n");
        Console.WriteLine("If there is a change in either bug I will give you the option of");
        Console.WriteLine("seeing the pictures of the bugs. The numbers stand for parts as");
        Console.WriteLine("follows:\n");
        PrintPartsTable();
        Console.Write("\nPress Enter to start. ");
        Console.ReadKey();
    }

    // Print the given bug.
    //
    static void PrintBug(ABug bug)
    {
        if (bug.feelers > 0)
        {
            for (int i = 0; i < FEELER_LENGTH; i++)
            {
                Console.Write("        ");
                for (int j = 0; j < bug.feelers; j++)
                {
                    Console.Write($" {bug.feelerType}");
                }
                Console.WriteLine();
            }
        }
        if (bug.head)
        {
            Console.WriteLine("        HHHHHHH");
            Console.WriteLine("        H     H");
            Console.WriteLine("        H O O H");
            Console.WriteLine("        H     H");
            Console.WriteLine("        H  V  H");
            Console.WriteLine("        HHHHHHH");
        }
        if (bug.neck)
        {
            for (int i = 0; i < NECK_LENGTH; i++)
            {
                Console.WriteLine("          N N");
            }
        }
        if (bug.body)
        {
            Console.WriteLine("     BBBBBBBBBBBB");
            for (int i = 0; i < BODY_HEIGHT; i++)
            {
                Console.WriteLine("     B          B");
            }
            if (bug.tail)
            {
                Console.WriteLine("TTTTTB          B");
            }
            Console.WriteLine("     BBBBBBBBBBBB");
        }
        if (bug.legs > 0)
        {
            for (int i = 0; i < LEG_LENGTH; i++)
            {
                Console.Write("    ");
                for (int j = 0; j < bug.legs; j++)
                {
                    Console.Write(" L");
                }
                Console.WriteLine();
            }
        }
    }

    // Return `true` if the given bug is finished; otherwise return `false`.
    //
    static bool Finished(ABug bug)
    {
        return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS;
    }

    // Return a random number from 1 to 6 (inclusive).
    //
    static int Dice()
    {
        Random rand = new Random();
        return rand.Next(6) + 1;
    }

    // Array to convert a number to its equilavent text.
    //
    static string[] asText =
    {
        "no",
        "a",
        "two",
        "three",
        "four",
        "five",
        "six" }; // MAX_LEGS

    // Return a string containing the given number and noun in their proper
    // form.
    //
    static string Plural(int number, string noun)
    {
        return $"{asText[number]} {noun}{((number > 1) ? "s" : "")}";
    }

    // Add the given part to the given player's bug.
    //
    static bool AddPart(Part part, Player player)
    {
        bool changed = false;
        switch (part)
        {
        case Part.body:
            if (player.bug.body)
            {
                Console.WriteLine($", but {player.pronoun} already have a body.");
            }
            else
            {
                Console.WriteLine($"; {player.pronoun} now have a body:");
                player.bug.body = true;
                changed = true;
            }
            break;
        case Part.neck:
            if (player.bug.neck)
            {
                Console.WriteLine($", but {player.pronoun} already have a neck.");
            }
            else if (!player.bug.body)
            {
                Console.WriteLine($", but {player.pronoun} need a body first.");
            }
            else
            {
                Console.WriteLine($"; {player.pronoun} now have a neck:");
                player.bug.neck = true;
                changed = true;
            }
            break;
        case Part.head:
            if (player.bug.head)
            {
                Console.WriteLine($", but {player.pronoun} already have a head.");
            }
            else if (!player.bug.neck)
            {
                Console.WriteLine($", but {player.pronoun} need a a neck first.");
            }
            else
            {
                Console.WriteLine($"; {player.pronoun} now have a head:");
                player.bug.head = true;
                changed = true;
            }
            break;
        case Part.feeler:
            if (player.bug.feelers == MAX_FEELERS)
            {
                Console.WriteLine($", but {player.pronoun} have two feelers already.");
            }
            else if (!player.bug.head)
            {
                Console.WriteLine($", but {player.pronoun} need a head first.");
            }
            else
            {
                player.bug.feelers += 1;
                Console.Write($"; {player.pronoun} now have",
                    Plural(player.bug.feelers, "feeler"));
                Console.WriteLine(":");
                changed = true;
            }
            break;
        case Part.tail:
            if (player.bug.tail)
            {
                Console.WriteLine($", but {player.pronoun} already have a tail.");
            }
            else if (!player.bug.body)
            {
                Console.WriteLine($", but {player.pronoun} need a body first.");
            }
            else
            {
                Console.WriteLine($"; {player.pronoun} now have a tail:");
                player.bug.tail = true;
                changed = true;
            }
            break;
        case Part.leg:
            if (player.bug.legs == MAX_LEGS)
            {
                Console.WriteLine($", but {player.pronoun} have {asText[MAX_LEGS]} feet already.");
            }
            else if (!player.bug.body)
            {
                Console.WriteLine($", but {player.pronoun} need a body first.");
            }
            else
            {
                player.bug.legs += 1;
                Console.Write($"; {player.pronoun} now have {Plural(player.bug.legs, "leg")}");
                Console.WriteLine(":");
                changed = true;
            }
            break;
        }
        return changed;
    }

    // Ask the user to press the Enter key, wait for the input, then erase the;
    // prompt text.
    //
    static void Prompt()
    {
        Console.Write("Press Enter to roll the dice. ");
        Console.ReadKey();
    }

    // Play one turn for the given player, rolling the dice and updating his bug.
    //
    static void Turn(Player player)
    {
        Prompt();
        int number = Dice();
        Part part = (Part) number;
        Console.Write($"{Capitalized(player.pronoun)} rolled a {number} ({part})");
        if (AddPart(part, player))
        {
            Console.WriteLine();
            PrintBug(player.bug);
        }
        Console.WriteLine();
    }

    // Print a message about the winner.
    //
    static void PrintWinner()
    {
        if (Finished(human.bug) && Finished(computer.bug))
        {
            Console.WriteLine("Both of our bugs are finished in the same number of turns!");
        }
        else if (Finished(human.bug))
        {
            Console.WriteLine($"{human.possessive} bug is finished.");
        }
        else if (Finished(computer.bug))
        {
            Console.WriteLine($"{computer.possessive} bug is finished.");
        }
    }

    static bool GameOver()
    {
        return Finished(human.bug) || Finished(computer.bug);
    }

    // Execute the game loop.
    //
    static void Play()
    {
        Console.Clear();
        while (!GameOver())
        {
            Turn(human);
            Turn(computer);
        }
        PrintWinner();
    }

    // Init the players' data before a new game.
    //
    static void Init()
    {
        human.pronoun = "you";
        human.possessive = "Your";
        human.bug.feelerType = 'A';
        computer.pronoun = "I";
        computer.possessive = "My";
        computer.bug.feelerType = 'F';
    }

    static void Main()
    {
        Init();
        PrintCredits();
        PrintInstructions();
        Play();
        Console.WriteLine("I hope you enjoyed the game, play it again soon!!");
    }
}

En Chapel

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Chapel:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-04-07.

Last modified 20250407T0201+0200.
*/

import IO;
import Random;

record Bug {
    var body: bool;
    var neck: bool;
    var head: bool;
    var feelers: int;
    var feelerType: string;
    var tail: bool;
    var legs: int;
    var finished: bool;
}

record Player {
    var pronoun: string;
    var possessive: string;
    var bug: Bug;
}

var computer: Player;
var human: Player;

/// Bug body parts.
enum Part { body = 1, neck, head, feeler, tail, leg }

// Bug body attributes.
const bodyHeight = 2;
const feelerLength = 4;
const legLength = 2;
const maxFeelers = 2;
const maxLegs = 6;
const neckLength = 2;

/// Clear the screen, reset the attributes and move the cursor to the
/// home position.
proc clear() {
    write("\x1B[2J\x1B[0m\x1B[H");
}

/// Move the cursor up by the given number of rows (defaults to 1),
/// without changing the column position.
proc cursorUp(rows: int = 1) {
    write("\x1B[", rows, "A");
}

/// Erase the current line, without moving the cursor position.
proc eraseLine() {
    write("\x1B[2K");
}

/// Move the cursor to the previous row, without changing
/// the column position, and erase its line.
proc erasePreviousLine() {
    cursorUp();
    eraseLine();
}

proc pressEnter(prompt: string) {
    write(prompt);
    IO.stdout.flush();
    IO.readLine();
}

/// Clear the screen, display the credits and wait for a keypress.
proc printCredits() {
    clear();
    writeln("Bug\n");
    writeln("Original version in BASIC:");
    writeln("    Brian Leibowitz, 1978.");
    writeln("    Creative computing (Morristown, New Jersey, USA), 1978.\n");
    writeln("This version in Chapel:");
    writeln("    Copyright (c) 2025, Marcos Cruz (programandala.net)");
    writeln("    SPDX-License-Identifier: Fair\n");
    pressEnter("Press Enter to read the instructions. ");
}

const instructions: string = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
""";

proc leftJustified(s: string, width: int): string {
    return s + " " * (width - s.size);
}

/// Print a table with the bug parts' description.
proc printPartsTable() {

    const columns = 3;
    const columnWidth = 8;
    const columnSeparation = 2;

    // Headers
    var header: [0 .. 2] string = ["Number", "Part", "Quantity"];
    for i in 0 ..< columns {
        write(leftJustified(header[i], columnWidth + columnSeparation));
    }
    writeln();

    // Rulers
    for i in 1 .. columns {
        write(
                "-" * columnWidth,
                (if i < columns then " " * columnSeparation else "")
        );
    }
    writeln();

    // Data
    var partQuantity: [Part.body .. Part.leg] int;
    partQuantity[Part.body] = 1;
    partQuantity[Part.neck] = 1;
    partQuantity[Part.head] = 1;
    partQuantity[Part.feeler] = 2;
    partQuantity[Part.tail] = 1;
    partQuantity[Part.leg] = 6;
    for part in Part {
        writeln(
            leftJustified((part: int): string, columnWidth + columnSeparation),
            leftJustified(((part): string).toTitle(), columnWidth + columnSeparation),
            partQuantity[part]
        );
    }

}

/// Clear the screen, print the instructions and wait for a keypress.
proc printInstructions() {
    clear();
    writeln("Bug");
    writeln(instructions);
    printPartsTable();
    pressEnter("\nPress Enter to start. ");
}

/// Print a bug head.
proc printHead() {
    writeln("        HHHHHHH");
    writeln("        H     H");
    writeln("        H O O H");
    writeln("        H     H");
    writeln("        H  V  H");
    writeln("        HHHHHHH");
}

/// Print the given bug.
proc printBug(bug: Bug) {
    if bug.feelers {
        for 0 ..< feelerLength {
            write("        ");
            for 0 ..< bug.feelers {
                write(" ", bug.feelerType);
            }
            writeln();
        }
    }
    if bug.head {
        printHead();
    }
    if bug.neck {
        for 0 ..< neckLength {
            writeln("          N N");
        }
    }
    if bug.body {
        writeln("     BBBBBBBBBBBB");
        for 0 ..< bodyHeight {
            writeln("     B          B");
        }
        if bug.tail {
            writeln("TTTTTB          B");
        }
        writeln("     BBBBBBBBBBBB");
    }
    if bug.legs {
        for 0 ..< legLength {
            write("    ");
            for 0 ..< bug.legs {
                write(" L");
            }
            writeln();
        }
    }
}

/// Return `true` if the given bug is finished; otherwise return `false`.
proc finished(bug: Bug): bool {
    return bug.feelers == maxFeelers && bug.tail && bug.legs == maxLegs;
}

var rsInt = new Random.randomStream(int);

/// Return a random number between 1 and 6 (inclusive).
proc dice(): int {
    return rsInt.next(1, 6);
}

/// Array to convert a number to its equilavent text.
var asText: [0 .. 6] string = [
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" ]; // maxLegs

/// Return a string containing the given number
/// and the given noun in their proper form.
proc plural(number: int, noun: string): string {
    return asText[number] + " " + noun + (if number > 1 then "s" else "");
}

/// Add the given part to the given player's bug.
proc addPart(part: Part, inout player: Player): bool {
    var changed: bool = false;
    select part {
        when Part.body {
            if player.bug.body {
                writeln(", but ", player.pronoun, " already have a body.");
            } else {
                writeln("; ", player.pronoun, " now have a body:");
                player.bug.body = true;
                changed = true;
            }
        }
        when Part.neck {
            if player.bug.neck {
                writeln(", but ", player.pronoun, " you already have a neck.");
            } else if !player.bug.body {
                writeln(", but ", player.pronoun, " need a body first.");
            } else {
                writeln("; ", player.pronoun, " now have a neck:");
                player.bug.neck = true;
                changed = true;
            }
        }
        when Part.head {
            if player.bug.head {
                writeln(", but ", player.pronoun, " already have a head.");
            } else if !player.bug.neck {
                writeln(", but ", player.pronoun, " need a a neck first.");
            } else {
                writeln("; ", player.pronoun, " now have a head:");
                player.bug.head = true;
                changed = true;
            }
        }
        when Part.feeler {
            if player.bug.feelers == maxFeelers {
                writeln(", but ", player.pronoun, " have two feelers already.");
            } else if !player.bug.head {
                writeln(", but ", player.pronoun, " need a head first.");
            } else {
                player.bug.feelers += 1;
                writeln("; ", player.pronoun, " now have ",
                        plural(player.bug.feelers, "feeler"), ":");
                changed = true;
            }
        }
        when Part.tail {
            if player.bug.tail {
                writeln(", but ", player.pronoun, " already have a tail.");
            } else if !player.bug.body {
                writeln(", but ", player.pronoun, " need a body first.");
            } else {
                writeln("; ", player.pronoun, " now have a tail:");
                player.bug.tail = true;
                changed = true;
            }
        }
        when Part.leg {
            if player.bug.legs == maxLegs {
                writeln(", but ", player.pronoun, " have ",
                        asText[maxLegs], " feet already.");
            } else if !player.bug.body {
                writeln(", but ", player.pronoun, " need a body first.");
            } else {
                player.bug.legs += 1;
                writeln("; ", player.pronoun, " now have ",
                        plural(player.bug.legs, "leg"), ":");
                changed = true;
            }
        }
    }
    return changed;
}

/// Ask the user to press the Enter key, wait for the input
/// and then erase the prompt text.
proc prompt() {
    pressEnter("Press Enter to roll the dice. ");
    erasePreviousLine();
}

/// Play one turn for the given player, rolling the dice
/// and updating his bug.
proc turn(inout player: Player) {
    prompt();
    var number: int = dice();
    var part: Part = number: Part;
    write((player.pronoun).toTitle(), " rolled a ", number, " (", part, ")");
    if addPart(part, player) {
            writeln();
            printBug(player.bug);
            player.bug.finished = finished(player.bug);
    }
    writeln();
}

/// Print a message about the winner.
proc printWinner() {
    select true {
        when human.bug.finished && computer.bug.finished do
            writeln("Both of our bugs are finished in the same number of turns!");
        when finished(human.bug) do
            writeln(human.possessive, " bug is finished.");
        when finished(computer.bug) do
            writeln(computer.possessive, " bug is finished.");
    }
}

/// Return `true` if either bug is finished, i.e. the game ending condition.
proc gameOver(): bool {
    return human.bug.finished || computer.bug.finished;
}

/// Execute the game loop.
proc play() {
    clear();
    do {
        turn(human);
        turn(computer);
    } while !gameOver();
    printWinner();
}

/// Init player data before a new game.
proc initData() {
    human.pronoun = "you";
    human.possessive = "Your";
    human.bug.feelerType = 'A';
    human.bug.finished = false;
    computer.pronoun = "I";
    computer.possessive = "My";
    computer.bug.feelerType = 'F';
    computer.bug.finished = false;
}

proc main() {
    initData();
    printCredits();
    printInstructions();
    play();
    writeln("I hope you enjoyed the game, play it again soon!!");
}

En Crystal

# Bug

# Original version in BASIC:
#   Brian Leibowitz, 1978.
#   Creative Computing (Morristown, New Jersey, USA), 1978.

# This version in Crystal:
#   Copyright (c) 2023, 2024, Marcos Cruz (programandala.net)
#   SPDX-License-Identifier: Fair
#
# Written in 2023-09, 2023-10, 2023-11, 2024-07.

# Last modified: 20251228T0052+0100.

class Bug
  property body : Bool
  property neck : Bool
  property head : Bool
  property feelers : Int32
  property feeler_type : Char
  property tail : Bool
  property legs : Int32

  def initialize
    @body = false
    @neck = false
    @head = false
    @feelers = 0
    @feeler_type = '|'
    @tail = false
    @legs = 0
  end
end

class Player
  property pronoun : String
  property possessive : String
  property bug : Bug

  def initialize
    @pronoun = ""
    @possessive = ""
    @bug = Bug.new
  end
end

# Bug body parts.
enum Part
  Body   = 1
  Neck
  Head
  Feeler
  Tail
  Leg
end

FIRST_PART = Part::Body.value
LAST_PART  = Part::Leg.value

PART_NAME     = ["body", "neck", "head", "feeler", "tail", "leg"]
PART_QUANTITY = [1, 1, 1, 2, 1, 6]

# Bug body attributes.
BODY_HEIGHT   = 2
FEELER_LENGTH = 4
LEG_LENGTH    = 2
MAX_FEELERS   = 2
MAX_LEGS      = 6
NECK_LENGTH   = 2

# Moves the cursor to the home position.
def home
  print "\e[H"
end

# Clears the screen and moves the cursor to the home position.
def clear
  print "\e[2J"
  home
end

# Moves the cursor up by the given number of rows (defaults to 1), without
# changing the column position.
def cursor_up(rows = 1)
  print "\e[#{rows}A"
end

# Erases the current line, without moving the cursor position.
def erase_line
  print "\e[2K"
end

# Moves the cursor to the previous row, without changing the column position,
# and erases its line.
def erase_previous_line
  cursor_up
  erase_line
end

# Prompts the user to enter a command and returns it.
def command(prompt = "> ") : String
  s = nil
  while s.is_a?(Nil)
    print prompt
    s = gets
  end
  s
end

# Prints the given prompt and waits until the user enters an empty string.
def press_enter(prompt : String)
  until command(prompt) == ""
  end
end

# Clears the screen, displays the credits and waits for the Enter key to be
# pressed.
def print_credits
  clear
  puts "Bug"
  puts
  puts "Original version in BASIC:"
  puts "    Brian Leibowitz, 1978."
  puts "    Creative computing (Morristown, New Jersey, USA), 1978."
  puts
  puts "This version in Crystal:"
  puts "    Copyright (c) 2023, 2024, Marcos Cruz (programandala.net)"
  puts "    SPDX-License-Identifier: Fair"
  puts
  press_enter "Press Enter to read the instructions. "
end

INSTRUCTIONS = "
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
"

COLUMNS           = 3
COLUMN_WIDTH      = 8
COLUMN_SEPARATION = 2

# Prints a table with the bug parts' description.
def print_parts_table
  # Headers
  header = ["Number", "Part", "Quantity"]
  (0...COLUMNS).each do |i|
    print header[i].ljust(COLUMN_WIDTH + COLUMN_SEPARATION)
  end
  puts

  # Rulers
  (0...COLUMNS).each do |i|
    print "-" * COLUMN_WIDTH, i == COLUMNS - 1 ? "" : " " * COLUMN_SEPARATION
  end
  puts

  # Data
  (FIRST_PART..LAST_PART).each do |n|
    print \
      n.to_s.ljust(COLUMN_WIDTH + COLUMN_SEPARATION),
      PART_NAME[n - 1].capitalize.ljust(COLUMN_WIDTH + COLUMN_SEPARATION),
      PART_QUANTITY[n - 1], "\n"
  end
end

# Clears the screen, prints the instructions and waits for a keypress.
def print_instructions
  clear
  puts "Bug"
  puts INSTRUCTIONS, "\n"
  print_parts_table
  press_enter "\nPress Enter to start. "
end

# Prints a bug head.
def print_head
  puts "        HHHHHHH"
  puts "        H     H"
  puts "        H O O H"
  puts "        H     H"
  puts "        H  V  H"
  puts "        HHHHHHH"
end

# Prints the given bug.
def print_bug(bug : Bug)
  if bug.feelers > 0
    (0...FEELER_LENGTH).each do |i|
      print "        "
      (0...bug.feelers).each do |j|
        print " #{bug.feeler_type}"
      end
      puts
    end
  end
  if bug.head
    print_head
  end
  if bug.neck
    (0...NECK_LENGTH).each do |i|
      puts "          N N"
    end
  end
  if bug.body
    puts "     BBBBBBBBBBBB"
    (0...BODY_HEIGHT).each do |i|
      puts "     B          B"
    end
    if bug.tail
      puts "TTTTTB          B"
    end
    puts "     BBBBBBBBBBBB"
  end
  if bug.legs > 0
    (0...LEG_LENGTH).each do |i|
      print "    "
      (0...bug.legs).each do |j|
        print " L"
      end
      puts
    end
  end
end

# Returns `true` if the given bug is finished; otherwise returns `false`.
def finished?(bug : Bug) : Bool
  bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS
end

# Array to convert a number to its equilavent text.
AS_TEXT = ["no", "a", "two", "three", "four", "five", "six"]

# Returns a string containing the given number and noun in their proper form.
def plural(number : Int, noun : String) : String
  AS_TEXT[number] + " " + noun + (number > 1 ? "s" : "")
end

# Adds the given part to the given player's bug.
def add_part(part : Int32, player : Player) : Bool
  changed = false
  case part
  when Part::Body.value
    if player.bug.body
      puts ", but #{player.pronoun} already have a body."
    else
      puts "; #{player.pronoun} now have a body:"
      player.bug.body = true
      changed = true
    end
  when Part::Neck.value
    if player.bug.neck
      puts ", but #{player.pronoun} already have a neck."
    elsif !player.bug.body
      puts ", but #{player.pronoun} need a body first."
    else
      puts ";  #{player.pronoun} now have a neck:"
      player.bug.neck = true
      changed = true
    end
  when Part::Head.value
    if player.bug.head
      puts ", but  #{player.pronoun} already have a head."
    elsif !player.bug.neck
      puts ", but  #{player.pronoun} need a a neck first."
    else
      puts ";  #{player.pronoun} now have a head:"
      player.bug.head = true
      changed = true
    end
  when Part::Feeler.value
    if player.bug.feelers == MAX_FEELERS
      puts ", but  #{player.pronoun} have two feelers already."
    elsif !player.bug.head
      puts ", but  #{player.pronoun} need a head first."
    else
      player.bug.feelers += 1
      print "; #{player.pronoun} now have #{plural(player.bug.feelers, "feeler")}"
      puts ":"
      changed = true
    end
  when Part::Tail.value
    if player.bug.tail
      puts ", but  #{player.pronoun} already have a tail."
    elsif !player.bug.body
      puts ", but  #{player.pronoun} need a body first."
    else
      puts ";  #{player.pronoun} now have a tail:"
      player.bug.tail = true
      changed = true
    end
  when Part::Leg.value
    if player.bug.legs == MAX_LEGS
      print ", but #{player.pronoun} have #{AS_TEXT[MAX_LEGS]} feet already."
    elsif !player.bug.body
      puts ", but  #{player.pronoun} need a body first."
    else
      player.bug.legs += 1
      print "; #{player.pronoun} now have #{plural(player.bug.legs, "leg")}"
      puts ":"
      changed = true
    end
  end
  changed
end

# Asks the user to press the Enter key, waits for the input, then erases the
# prompt text.
def prompt
  press_enter "Press Enter to roll the dice. "
  erase_previous_line
end

# Plays one turn for the given player, rolling the dice and updating his bug.
def turn(player : Player)
  prompt
  part = rand(FIRST_PART..LAST_PART) # dice
  print "#{player.pronoun.capitalize} rolled a #{part} (#{PART_NAME[part - 1]})"
  if add_part(part, player)
    puts
    print_bug(player.bug)
  end
  puts
end

# Prints a message about the winner.
def print_winner(p1, p2 : Player)
  if finished?(p1.bug) && finished?(p2.bug)
    puts "Both of our bugs are finished in the same number of turns!"
  elsif finished?(p1.bug)
    puts "#{p1.possessive} bug is finished."
  elsif finished?(p2.bug)
    puts "#{p2.possessive} bug is finished."
  end
end

# Inits the data of a player.
def init(player : Player, pronoun : String, possessive : String, feeler_type : Char)
  player.pronoun = pronoun
  player.possessive = possessive
  player.bug.feeler_type = feeler_type
end

# Executes the game loop.
def play
  clear
  computer = Player.new
  human = Player.new
  init human, "you", "your", 'A'
  init computer, "I", "My", 'F'
  while !(finished?(human.bug) || finished?(computer.bug))
    turn human
    turn computer
  end
  print_winner human, computer
end

print_credits
print_instructions
play
puts "I hope you enjoyed the game, play it again soon!!"

En D

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in D:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written in 2023-03-26/29.

Last modified 20251220T0637+0100.
*/

module bug;

/// Bug type.

struct Bug
{
    bool body;
    bool neck;
    bool head;
    int feelers;
    char feelerType;
    bool tail;
    int legs;
    bool finished;
}

/// Player type.

struct Player
{
    string pronoun;
    string possessive;
    Bug bug;
}

/// Players.

Player computer, human;

/// Bug body parts.

enum Part { body = 1, neck, head, feeler, tail, leg }

// Bug body attributes.

enum bodyHeight = 2;
enum feelerLength = 4;
enum legLength = 2;
enum maxFeelers = 2;
enum maxLegs = 6;
enum neckLength = 2;

/// Clear the screen, reset the attributes and move the cursor to the
/// home position.

void clear()
{
    import std.stdio : write;

    write("\033[2J\033[0m\033[H");
}

/// Move the cursor up by the given number of rows (defaults to 1),
/// without changing the column position.

void cursorUp(int rows = 1)
{
    import std.stdio : write;

    write("\033[", rows, "A");
}

/// Erase the current line, without moving the cursor position.

void eraseLine()
{
    import std.stdio : write;

    write("\033[2K");
}

/// Move the cursor to the previous row, without changing
/// the column position, and erase its line.

void erasePreviousLine()
{
    cursorUp();
    eraseLine();
}

/// Clear the screen, display the credits and wait for a keypress.

void printCredits()
{
    import std.stdio : readln;
    import std.stdio : write;
    import std.stdio : writeln;

    clear();
    writeln("Bug\n");
    writeln("Original version in BASIC:");
    writeln("    Brian Leibowitz, 1978.");
    writeln("    Creative computing (Morristown, New Jersey, USA), 1978.\n");
    writeln("This version in D:");
    writeln("    Copyright (c) 2023, Marcos Cruz (programandala.net)");
    writeln("    SPDX-License-Identifier: Fair\n");
    write("Press Enter to read the instructions. ");
    readln();
}

string instructions = "
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
";

/// Print a table with the bug parts' description.

void printPartsTable()
{

    import std.array : replicate;
    import std.conv : to;
    import std.stdio : write;
    import std.stdio : writeln;
    import std.string : capitalize, leftJustify;

    enum columns = 3;
    enum columnWidth = 8;
    enum columnSeparation = 2;

    // Headers
    string[] header = ["Number", "Part", "Quantity"];
    foreach (i; 0 .. columns)
    {
        write(leftJustify(header[i], columnWidth + columnSeparation));
    }
    writeln();

    // Rulers
    foreach (i; 0 .. columns)
    {
        write(
                replicate("-", columnWidth),
                (i == columns - 1 ? "" : replicate(" ", columnSeparation)));
    }
    writeln();

    // Data
    int[Part] partQuantity;
    partQuantity[Part.body] = 1;
    partQuantity[Part.neck] = 1;
    partQuantity[Part.head] = 1;
    partQuantity[Part.feeler] = 2;
    partQuantity[Part.tail] = 1;
    partQuantity[Part.leg] = 6;
    for (Part part = Part.min; part <= Part.max; ++part)
    {
        writeln(
                leftJustify(to!string(to!int(part)), columnWidth + columnSeparation),
                leftJustify(capitalize(to!string(part)), columnWidth + columnSeparation),
                partQuantity[part]
                );
    }

}

/// Clear the screen, print the instructions and wait for a keypress.

void printInstructions()
{
    import std.stdio : readln;
    import std.stdio : write;
    import std.stdio : writeln;

    clear();
    writeln("Bug");
    writeln(instructions);
    printPartsTable();
    write("\nPress Enter to start. ");
    readln();
}

/// Print a bug head.

void printHead()
{
    import std.stdio : writeln;

    writeln("        HHHHHHH");
    writeln("        H     H");
    writeln("        H O O H");
    writeln("        H     H");
    writeln("        H  V  H");
    writeln("        HHHHHHH");
}

/// Print the given bug.

void printBug(Bug bug)
{
    import std.stdio : write;
    import std.stdio : writeln;

    if (bug.feelers)
    {
        foreach (i; 0 .. feelerLength)
        {
            write("        ");
            foreach (j; 0 .. bug.feelers)
            {
                write(" ", bug.feelerType);
            }
            writeln();
        }
    }
    if (bug.head)
    {
        printHead();
    }
    if (bug.neck)
    {
        foreach (i; 0 .. neckLength)
        {
            writeln("          N N");
        }
    }
    if (bug.body)
    {
        writeln("     BBBBBBBBBBBB");
        foreach (i; 0 .. bodyHeight)
        {
            writeln("     B          B");
        }
        if (bug.tail)
        {
            writeln("TTTTTB          B");
        }
        writeln("     BBBBBBBBBBBB");
    }
    if (bug.legs)
    {
        foreach (i; 0 .. legLength)
        {
            write("    ");
            foreach (j; 0 .. bug.legs)
            {
                write(" L");
            }
            writeln();
        }
    }
}

/// Return `true` if the given bug is finished; otherwise return `false`.

bool finished(Bug bug)
{
    return bug.feelers == maxFeelers && bug.tail && bug.legs == maxLegs;
}

/// Return a random number between 1 and 6 (inclusive).

int dice()
{
    import std.random : uniform;

    return uniform(1, 7);
}

/// Array to convert a number to its equilavent text.

string[] asText = [
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" ]; // maxLegs

/// Return a string containing the given number
/// and the given noun in their proper form.

string plural(int number, string noun)
{
    return asText[number] ~ " " ~ noun ~ ((number > 1) ? "s" : "");
}

/// Add the given part to the given player's bug.

bool addPart(Part part, ref Player player)
{
    import std.stdio : writeln;

    bool changed = false;
    final switch (part)
    {
    case Part.body:
        if (player.bug.body)
        {
            writeln(", but ", player.pronoun, " already have a body.");
        }
        else
        {
            writeln("; ", player.pronoun, " now have a body:");
            player.bug.body = true;
            changed = true;
        }
        break;
    case Part.neck:
        if (player.bug.neck)
        {
            writeln(", but ", player.pronoun, " you already have a neck.");
        }
        else if (!player.bug.body)
        {
            writeln(", but ", player.pronoun, " need a body first.");
        }
        else
        {
            writeln("; ", player.pronoun, " now have a neck:");
            player.bug.neck = true;
            changed = true;
        }
        break;
    case Part.head:
        if (player.bug.head)
        {
            writeln(", but ", player.pronoun, " already have a head.");
        }
        else if (!player.bug.neck)
        {
            writeln(", but ", player.pronoun, " need a a neck first.");
        }
        else
        {
            writeln("; ", player.pronoun, " now have a head:");
            player.bug.head = true;
            changed = true;
        }
        break;
    case Part.feeler:
        if (player.bug.feelers == maxFeelers)
        {
            writeln(", but ", player.pronoun, " have two feelers already.");
        }
        else if (!player.bug.head)
        {
            writeln(", but ", player.pronoun, " need a head first.");
        }
        else
        {
            player.bug.feelers++;
            writeln("; ", player.pronoun, " now have ",
                    plural(player.bug.feelers, "feeler"), ":");
            changed = true;
        }
        break;
    case Part.tail:
        if (player.bug.tail)
        {
            writeln(", but ", player.pronoun, " already have a tail.");
        }
        else if (!player.bug.body)
        {
            writeln(", but ", player.pronoun, " need a body first.");
        }
        else
        {
            writeln("; ", player.pronoun, " now have a tail:");
            player.bug.tail = true;
            changed = true;
        }
        break;
    case Part.leg:
        if (player.bug.legs == maxLegs)
        {
            writeln(", but ", player.pronoun, " have ",
                    asText[maxLegs], " feet already.");
        }
        else if (!player.bug.body)
        {
            writeln(", but ", player.pronoun, " need a body first.");
        }
        else
        {
            player.bug.legs++;
            writeln("; ", player.pronoun, " now have ",
                    plural(player.bug.legs, "leg"), ":");
            changed = true;
        }
        break;
    }
    return changed;
}

/// Ask the user to press the Enter key, wait for the input
/// and then erase the prompt text.

void prompt()
{
    import std.stdio : readln;
    import std.stdio : write;

    write("Press Enter to roll the dice. ");
    readln();
    erasePreviousLine();
}

/// Play one turn for the given player, rolling the dice
/// and updating his bug.

void turn(ref Player player)
{
    import std.stdio : write;
    import std.stdio : writeln;
    import std.string : capitalize;
    import std.string : strip;

    prompt();
    int number = dice();
    Part part = cast(Part)number;
    write(capitalize(player.pronoun), " rolled a ", number, " (", part, ")");
    if (addPart(part, player))
    {
            writeln();
            printBug(player.bug);
            player.bug.finished = finished(player.bug);
    }
    writeln();
}

/// Print a message about the winner.

void printWinner()
{
    import std.stdio : writeln;

    if (human.bug.finished && computer.bug.finished)
    {
        writeln("Both of our bugs are finished in the same number of turns!");
    }
    else if (finished(human.bug))
    {
        writeln(human.possessive, " bug is finished.");
    }
    else if (finished(computer.bug))
    {
        writeln(computer.possessive, " bug is finished.");
    }
}

/// Return `true` if either bug is finished, i.e. the game ending condition.

bool gameOver()
{
    return human.bug.finished || computer.bug.finished;
}

/// Execute the game loop.

void play()
{
    clear();
    do
    {
        turn(human);
        turn(computer);
    } while (!gameOver());
    printWinner();
}

/// Init player data before a new game.

void init()
{
    human.pronoun = "you";
    human.possessive = "Your";
    human.bug.feelerType = 'A';
    human.bug.finished = false;
    computer.pronoun = "I";
    computer.possessive = "My";
    computer.bug.feelerType = 'F';
    computer.bug.finished = false;
}

void main()
{
    import std.stdio : writeln;

    init();
    printCredits();
    printInstructions();
    play();
    writeln("I hope you enjoyed the game, play it again soon!!");
}

En Hare

// Bug
//
// Original version in BASIC:
//      Brian Leibowitz, 1978.
//      Creative Computing (Morristown, New Jersey, USA), 1978.
//
// This version in Hare:
//      Copyright (c) 2025, Marcos Cruz (programandala.net)
//      SPDX-License-Identifier: Fair
//
// Written in 2025-02-12/15.
//
// Last modified: 20260213T1645+0100.

// Modules {{{1
// =============================================================================

use ascii;
use bufio;
use fmt;
use math::random;
use os;
use strconv;
use strings;
use time;

// Data {{{1
// =============================================================================

type Bug = struct {
        body: bool,
        neck: bool,
        head: bool,
        feelers: int,
        feeler_type: rune,
        tail: bool,
        legs: int,
};

type Player = struct {
        pronoun: str,
        possessive: str,
        bug: Bug,
};

let computer = Player {
        pronoun = "I",
        possessive = "My",
        bug = Bug {
                body = false,
                neck = false,
                head = false,
                feelers = 0,
                feeler_type = 'F',
                tail = false,
                legs = 0,
        }
};

let human = Player {
        pronoun = "you",
        possessive = "Your",
        bug = Bug {
                body = false,
                neck = false,
                head = false,
                feelers = 0,
                feeler_type = 'A',
                tail = false,
                legs = 0,
        }
};

type bug_part = enum int {BODY = 1, NECK, HEAD, FEELER, TAIL, LEG};

def FIRST_PART = bug_part::BODY: int;
def LAST_PART = bug_part::LEG: int;

fn part_quantity(part: bug_part) int = {
        return switch (part) {
                case bug_part::BODY => yield 1;
                case bug_part::NECK => yield 1;
                case bug_part::HEAD => yield 1;
                case bug_part::FEELER => yield 2;
                case bug_part::TAIL => yield 1;
                case bug_part::LEG => yield 6;
        };
};

fn part_name(part: bug_part) str = {
        return switch (part) {
                case bug_part::BODY => yield "body";
                case bug_part::NECK => yield "neck";
                case bug_part::HEAD => yield "head";
                case bug_part::FEELER => yield "feeler";
                case bug_part::TAIL => yield "tail";
                case bug_part::LEG => yield "leg";
        };
};

// Bug body attributes.
//
def BODY_HEIGHT = 2;
def FEELER_LENGTH = 4;
def LEG_LENGTH = 2;
def MAX_FEELERS = 2;
def MAX_LEGS = 6;
def NECK_LENGTH = 2;

// Terminal {{{1
// =============================================================================

def NORMAL_STYLE = 0;

fn move_cursor_home() void = {
        fmt::print("\x1B[H")!;
};

fn move_cursor_up(lines: int = 1) void = {
        fmt::printf("\x1B[{}A", lines)!;
};

fn set_style(style: int) void = {
        fmt::printf("\x1B[{}m", style)!;
};

fn reset_attributes() void = {
        set_style(NORMAL_STYLE);
};

fn erase_screen() void = {
        fmt::print("\x1B[2J")!;
};

fn clear_screen() void = {
        erase_screen();
        reset_attributes();
        move_cursor_home();
};

// Erase the entire current line and move the cursor to the start of the line.
//
fn erase_line() void = {
        fmt::print("\x1B[2K")!;
};

// User input {{{1
// =============================================================================

fn print_prompt(prompt: str = "") void = {
        fmt::print(prompt)!;
        bufio::flush(os::stdout)!;
};

fn accept_string(prompt: str = "") str = {
        print_prompt(prompt);
        const buffer = match (bufio::read_line(os::stdin)!) {
                case let buffer: []u8 =>
                        yield buffer;
                case =>
                        return "";
                };
        defer free(buffer);
        return strings::dup(strings::fromutf8(buffer)!)!;
};

fn press_enter(prompt: str = "") void = {
        free(accept_string(prompt));
};

// Credits and instructions {{{1
// =============================================================================

// Clear the screen, display the credits and wait for a keypress.
//
fn print_credits() void = {
        clear_screen();
        fmt::println("Bug\n")!;
        fmt::println("Original version in BASIC:")!;
        fmt::println("    Brian Leibowitz, 1978.")!;
        fmt::println("    Creative computing (Morristown, New Jersey, USA), 1978.\n")!;
        fmt::println("This version in Hare:")!;
        fmt::println("    Copyright (c) 2025, Marcos Cruz (programandala.net)")!;
        fmt::println("    SPDX-License-Identifier: Fair\n")!;
        press_enter("Press Enter to read the instructions. ");
};

def INSTRUCTIONS = `
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
`;

// Clear the screen, print the instructions and wait for a keypress.
//
fn print_instructions() void = {
        clear_screen();
        fmt::println("Bug")!;
        fmt::println(INSTRUCTIONS)!;
        print_parts_table();
        press_enter("\nPress Enter to start. ");
};

// Strings {{{1
// =============================================================================

fn to_capital(s: str) str = {
        const initial = ascii::strupper(strings::sub(s, 0, 1))!;
        defer free(initial);
        return strings::concat(initial, strings::sub(s, 1))!;
};

fn repeat(s: str, n: int) str = {
        let result = "";
        for (let i: int = 0; i < n; i += 1) {
                result = strings::concat(result, s)!;
        };
        return result;
};

// Pseudo-random numbers {{{1
// =============================================================================

let rand: random::random = 0;

// Return a random number from 1 to 6 (inclusive).
//
fn dice(r: *random::random) int = {
        return (random::u32n(r, 6) + 1): int;
};

fn randomize() void = {
        rand = random::init(time::now(time::clock::MONOTONIC).sec: u64);
};

// Main {{{1
// =============================================================================

// Print a table with the description of the bug parts.
//
fn print_parts_table() void = {
        def COLUMNS = 3;
        def COLUMN_WIDTH = 8;
        def COLUMN_SEPARATION = 2;

        const format_string = strings::concat(
                "{:-",
                strconv::itos(COLUMN_WIDTH + COLUMN_SEPARATION),
                "}")!;

        // Headers
        const header: [_]str = ["Number", "Part", "Quantity"];
        for (let i = 0; i < COLUMNS; i += 1) {
                fmt::printf(format_string, header[i])!;
        };
        fmt::println()!;

        // Rulers
        const ruler = repeat("-", COLUMN_WIDTH);
        defer free(ruler);
        const padding = repeat(" ", COLUMN_SEPARATION - 1);
        defer free(padding);
        for (let i = 0; i < COLUMNS; i += 1) {
                fmt::print(ruler, if (i == COLUMNS - 1) "" else padding)!;
        };
        fmt::println()!;

        // Data
        for (let p = FIRST_PART; p <= LAST_PART; p += 1) {
                let part = p: bug_part;
                fmt::printf(format_string, p)!;
                let name = to_capital(part_name(part));
                defer free(name);
                fmt::printf(format_string, name)!;
                fmt::printf(format_string, part_quantity(part))!;
                fmt::println()!;
        };
};

// Print a bug head.
//
fn print_head() void = {
        fmt::println("        HHHHHHH")!;
        fmt::println("        H     H")!;
        fmt::println("        H O O H")!;
        fmt::println("        H     H")!;
        fmt::println("        H  V  H")!;
        fmt::println("        HHHHHHH")!;
};

// Print the given bug.
//
fn print_bug(bug: Bug) void = {
        if (bug.feelers > 0) {
                for (let i = 0; i < FEELER_LENGTH; i += 1) {
                        fmt::print("        ")!;
                        for (let i = 0; i < bug.feelers; i += 1) {
                                fmt::print(" ", bug.feeler_type)!;
                        };
                        fmt::println()!;
                };
        };
        if (bug.head) {
                print_head();
        };
        if (bug.neck) {
                for (let i = 0; i < NECK_LENGTH; i += 1) {
                        fmt::println("          N N")!;
                };
        };
        if (bug.body) {
                fmt::println("     BBBBBBBBBBBB")!;
                for (let i = 0; i < BODY_HEIGHT; i += 1) {
                        fmt::println("     B          B")!;
                };
                if (bug.tail) {
                        fmt::println("TTTTTB          B")!;
                };
                fmt::println("     BBBBBBBBBBBB")!;
        };
        if (bug.legs > 0) {
                for (let i = 0; i < LEG_LENGTH; i += 1) {
                        fmt::print("    ")!;
                        for (let i = 0; i < bug.legs; i += 1) {
                                fmt::print(" L")!;
                        };
                        fmt::println()!;
                };
        };
};

// Return `true` if the given bug is finished; otherwise return `false`.
//
fn finished(bug: Bug) bool = {
        return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS;
};

// Array to convert a number to its equilavent text.
//
let as_text: [_]str = [
        "no",
        "a",
        "two",
        "three",
        "four",
        "five",
        "six" ]; // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
//
fn plural(number: int, noun: str) str = {
        return strings::concat(
                as_text[number],
                " ",
                noun,
                if (number > 1) "s" else ""
        )!;
};

// Add the given part to the given player's bug.
//
fn add_part(part: bug_part, player: *Player) bool = {
        let changed: bool = false;
        switch (part) {
        case bug_part::BODY =>
                if (player.bug.body) {
                        fmt::println(", but", player.pronoun, "already have a body.")!;
                } else {
                        fmt::println(";", player.pronoun, "now have a body:")!;
                        player.bug.body = true;
                        changed = true;
                };
        case bug_part::NECK =>
                if (player.bug.neck) {
                        fmt::println(", but", player.pronoun, "already have a neck.")!;
                } else if (!player.bug.body) {
                        fmt::println(", but", player.pronoun, "need a body first.")!;
                } else {
                        fmt::println(";", player.pronoun, "now have a neck:")!;
                        player.bug.neck = true;
                        changed = true;
                };
        case bug_part::HEAD =>
                if (player.bug.head) {
                        fmt::println(", but", player.pronoun, "already have a head.")!;
                } else if (!player.bug.neck) {
                        fmt::println(", but", player.pronoun, "need a a neck first.")!;
                } else {
                        fmt::println(";", player.pronoun, "now have a head:")!;
                        player.bug.head = true;
                        changed = true;
                };
        case bug_part::FEELER =>
                if (player.bug.feelers == MAX_FEELERS) {
                        fmt::println(", but", player.pronoun, "have two feelers already.")!;
                } else if (!player.bug.head) {
                        fmt::println(", but", player.pronoun, "need a head first.")!;
                } else {
                        player.bug.feelers += 1;
                        fmt::print(";", player.pronoun, "now have",
                                plural(player.bug.feelers, "feeler"))!;
                        fmt::println(":")!;
                        changed = true;
                };
        case bug_part::TAIL =>
                if (player.bug.tail) {
                        fmt::println(", but", player.pronoun, "already have a tail.")!;
                } else if (!player.bug.body) {
                        fmt::println(", but", player.pronoun, "need a body first.")!;
                } else {
                        fmt::println(";", player.pronoun, "now have a tail:")!;
                        player.bug.tail = true;
                        changed = true;
                };
        case bug_part::LEG =>
                if (player.bug.legs == MAX_LEGS) {
                        fmt::println(", but", player.pronoun, "have",
                                as_text[MAX_LEGS], "feet already.")!;
                } else if (!player.bug.body) {
                        fmt::println(", but", player.pronoun, "need a body first.")!;
                } else {
                        player.bug.legs += 1;
                        fmt::print(";", player.pronoun, "now have",
                                plural(player.bug.legs, "leg"))!;
                        fmt::println(":")!;
                        changed = true;
                };
        };
        return changed;
};

// Play one turn for the given player, rolling the dice and updating his bug.
//
fn turn(player: *Player) void = {
        press_enter("Press Enter to roll the dice. ");
        move_cursor_up(1);
        erase_line();

        const number: int = dice(&rand);
        const part = number: bug_part;
        const pronoun = to_capital(player.pronoun);
        defer free(pronoun);
        fmt::printf("{} rolled a {} ({})", pronoun, number, part_name(part))!;
        if (add_part(part, player)) {
                fmt::println()!;
                print_bug(player.bug);
        };
        fmt::println()!;
};

// Print a message about the winner.
//
fn print_winner() void = {
        if (finished(human.bug) && finished(computer.bug)) {
                fmt::println("Both of our bugs are finished in the same number of turns!")!;
        } else if (finished(human.bug)) {
                fmt::println(human.possessive, "bug is finished.")!;
        } else if (finished(computer.bug)) {
                fmt::println(computer.possessive, "bug is finished.")!;
        };
};

// Return `true` if either bug is finished, i.e. the game ending condition.
//
fn game_over() bool = {
        return finished(human.bug) || finished(computer.bug);
};

// Execute the game loop.
//
fn play() void = {
        clear_screen();
        for (!game_over()) {
                turn(&human);
                turn(&computer);
        };
        print_winner();
};

fn init() void = {
        randomize();
};

fn bye() void = {
        fmt::println("I hope you enjoyed the game, play it again soon!!")!;
};

export fn main() void = {
        init();
        print_credits();
        print_instructions();
        play();
        bye();
};

En Janet

# Bug

# Original version in BASIC:
#   Brian Leibowitz, 1978.
#   Creative Computing (Morristown, New Jersey, USA), 1978.

# This version in Janet:
#   Copyright (c) 2025, Marcos Cruz (programandala.net)
#   SPDX-License-Identifier: Fair
#
# Written in 2025-12-27/30.

# Last modified: 20251230T1346+0100.

(def player-model
  @{:pronoun ""
    :possesive ""
    :body false
    :neck false
    :head false
    :feelers 0
    :feeler-type "|"
    :tail false
    :legs 0})

(def player
  @{:human
      (table/setproto
        @{:pronoun "you"
          :possessive "your"
          :feeler-type "A"}
        player-model)
    :computer
      (table/setproto
        @{:pronoun "I"
          :possessive "my"
          :feeler-type "F"}
        player-model)})

(def body 1)
(def neck 2)
(def head 3)
(def feeler 4)
(def tail 5)
(def leg 6)

(def first-part body)
(def last-part leg)

(def part-name ["body" "neck" "head" "feeler" "tail" "leg"])
(def part-quantity [1 1 1 2 1 6])

(def body-height 2)
(def feeler-length 4)
(def leg-length 2)
(def max-feelers 2)
(def max-legs 6)
(def neck-length 2)

(defn move-cursor-home []
  (prin "\e[H"))

(defn clear-screen []
  (prin "\e[2J")
  (move-cursor-home))

(defn move-cursor-up [&opt rows]
  (default rows 1)
  (prin "\e[" rows "A"))

(defn erase-line []
  (prin "\e[2K"))

(defn erase-previous-line []
  (move-cursor-up)
  (erase-line))

(defn command [&opt prompt-text]
  (default prompt-text "> ")
  (prin prompt-text)
  (flush)
  (string/trim (getline)))

(defn press-enter [prompt-text]
  (unless (= (length (command prompt-text)) 0)) (break))

(defn print-credits []
  (clear-screen)
  (print "Bug\n")
  (print "Original version in BASIC:")
  (print "    Brian Leibowitz, 1978.")
  (print "    Creative computing (Morristown, New Jersey, USA), 1978.\n")
  (print "This version in Janet:")
  (print "    Copyright (c) 2025, Marcos Cruz (programandala.net)")
  (print "    SPDX-License-Identifier: Fair\n")
  (press-enter "Press Enter to read the instructions. "))

(def columns 3)
(def column-width 8)
(def column-separation 2)

(defn capitalized [s]
  (string (string/ascii-upper (string/slice s 0 1)) (string/slice s 1)))

(defn left-justified [s n]
  (string/format (string "%-" n "s") s))

(defn print-parts-table []

  (def width (+ column-width column-separation))

  # Headers
  (def header ["Number" "Part" "Quantity"])
  (for i 0 columns
    (prin (left-justified (get header i) width)))
  (print)

  # Rulers
  (for i 0 columns
    (prin
      (string/repeat "-" column-width)
      (unless (= i (- columns 1))
        (string/repeat " " column-separation))))
  (print)

  # Data
  (for n first-part (+ last-part 1)
    (print
      (left-justified (string n) width)
      (left-justified (capitalized (get part-name (- n 1))) width)
      (get part-quantity (- n 1)))))

(defn print-instructions []
  (clear-screen)
  (print "Bug\n")
  (print
    `The object is to finish your bug before I finish mine. Each number
    stands for a part of the bug body.

    I will roll the die for you, tell you what I rolled for you, what the
    number stands for, and if you can get the part. If you can get the
    part I will give it to you. The same will happen on my turn.

    If there is a change in either bug I will give you the option of
    seeing the pictures of the bugs. The numbers stand for parts as
    follows:`)
  (print)
  (print-parts-table)
  (press-enter "\nPress Enter to start. "))

(defn print-head []
  (print "        HHHHHHH")
  (print "        H     H")
  (print "        H O O H")
  (print "        H     H")
  (print "        H  V  H")
  (print "        HHHHHHH"))

(defn print-bug [player]
  (when (> (get player :feelers) 0)
    (for i 0 feeler-length
      (prin "        ")
      (for j 0 (get player :feelers)
        (prin " " (get player :feeler-type)))
      (print)))
  (when (get player :head)
    (print-head))
  (when (get player :neck)
    (for i 0 neck-length
      (print "          N N")))
  (when (get player :body)
    (print "     BBBBBBBBBBBB")
    (for i 0 body-height
      (print "     B          B"))
    (when (get player :tail)
      (print "TTTTTB          B"))
    (print "     BBBBBBBBBBBB"))
  (when (> (get player :legs) 0)
    (for i 0 leg-length
      (prin "    ")
      (for j 0 (get player :legs)
        (prin " L"))
      (print))))

(def as-text ["no" "a" "two" "three" "four" "five" "six"])

(defn plural [number noun]
  (string (get as-text number) " " noun (when (> number 1) "s")))

(defn add-part [part player]
  (var changed false)
  (case part
    body
      (if (get player :body)
        (print ", but " (get player :pronoun) " already have a body.")
        (do
          (print "; " (get player :pronoun) " now have a body:")
          (put player :body true)
          (set changed true)))
    neck
      (cond
        (get player :neck)
          (print ", but " (get player :pronoun) " already have a neck.")
        (not (get player :body))
          (print ", but " (get player :pronoun) " need a body first.")
        (do
          (print "; " (get player :pronoun) " now have a neck:")
          (put player :neck true)
          (set changed true)))
    head
      (cond
        (get player :head)
          (print ", but " (get player :pronoun) " already have a head.")
        (not (get player :neck))
          (print ", but " (get player :pronoun) " need a a neck first.")
        (do
          (print "; " (get player :pronoun) " now have a head:")
          (put player :head true)
          (set changed true)))
    feeler
      (cond
        (= (get player :feelers) max-feelers)
          (print ", but " (get player :pronoun) " have two feelers already.")
        (not (get player :head))
          (print ", but " (get player :pronoun) " need a head first.")
        (do
          (+= (player :feelers) 1)
          (prin
            "; " (get player :pronoun) " now have "
            (plural (get player :feelers) "feeler"))
          (print ":")
          (set changed true)))
    tail
      (cond
        (get player :tail)
          (print ", but " (get player :pronoun) " already have a tail.")
        (not (get player :body))
          (print ", but " (get player :pronoun) " need a body first.")
        (do
          (print "; " (get player :pronoun) " now have a tail:")
          (put player :tail true)
          (set changed true)))
    leg
      (cond
        (= (get player :legs) max-legs)
          (prin
            ", but " (get player :pronoun)
            " have " (get as-text max-legs) " feet already.")
        (not (get player :body))
          (print ", but " (get player :pronoun) " need a body first.")
        (do
          (+= (player :legs) 1)
          (prin
            "; " (get player :pronoun)
            " now have " (plural (get player :legs) "leg"))
          (print ":")
          (set changed true))))
  changed)

(defn prompt-player []
  (press-enter "Press Enter to roll the dice. ")
  (erase-previous-line))

(defn random-int-in-range [minimum maximum]
  (def random-number-generator (math/rng (os/cryptorand 16)))
  (+ (math/rng-int random-number-generator (- maximum minimum)) minimum))

(defn dice []
  (random-int-in-range first-part (+ 1 last-part)))

(defn turn [player]
  (prompt-player)
  (def part (dice))
  (prin
    (capitalized (get player :pronoun))
    " rolled a " part " (" (get part-name (- part 1)) ")")
  (when (add-part part player)
    (print)
    (print-bug player))
  (print))

(defn finished? [player-id]
  (and
    (= max-feelers (get (get player player-id) :feelers))
    (get (get player player-id) :tail)
    (= max-legs (get (get player player-id) :legs))))

(defn print-finished [player-id]
  (print (capitalized (get (get player player-id) :possessive)) " bug is finished."))

(defn print-winner []
  (cond
    (and (finished? :human) (finished? :computer))
      (print "Both of our bugs are finished in the same number of turns!")
    (finished? :human)
      (print-finished :human)
    (finished? :computer)
      (print-finished :computer)))

(defn play []
  (clear-screen)
  (while (not (or (finished? :human) (finished? :computer)))
    (turn (get player :human))
    (turn (get player :computer)))
  (print-winner))

(defn main [& args]
  (print-credits)
  (print-instructions)
  (play)
  (print "I hope you enjoyed the game, play it again soon!!"))

En Julia

# Bug

# Original version in BASIC:
#   Brian Leibowitz, 1978.
#   Creative Computing (Morristown, New Jersey, USA), 1978.

# This version in Julia:
#   Copyright (c) 2024, Marcos Cruz (programandala.net)
#   SPDX-License-Identifier: Fair
#
# Written on 2024-07-04.

# Last modified: 20240705T1630+0200.

mutable struct Bug

    body::Bool
    neck::Bool
    head::Bool
    feelers::Int
    feeler_type::Char
    tail::Bool
    legs::Int

end

mutable struct Player

    pronoun::String
    possessive::String
    bug::Bug

end

# Bug body parts.

@enum Part begin
    body = 1
    neck
    head
    feeler
    tail
    leg

end

const FIRST_PART = Int(body)
const LAST_PART  = Int(leg)
const PARTS      = LAST_PART

const PART_NAME     = ["body", "neck", "head", "feeler", "tail", "leg"]
const PART_QUANTITY = [1, 1, 1, 2, 1, 6]

# Bug body attributes.

const BODY_HEIGHT   = 2
const FEELER_LENGTH = 4
const LEG_LENGTH    = 2
const MAX_FEELERS   = 2
const MAX_LEGS      = 6
const NECK_LENGTH   = 2

# Move the cursor to the home position.

function home()

    print("\e[H")

end

# Clear the screen and move the cursor to the home position.

function clear_screen()

    print("\e[2J")
    home()

end

# Move the cursor up by the given number of rows (the default is 1), without
# changing the column position.

function cursor_up(rows = 1)

    print("\e[$(rows)A")

end

# Erase the current line, without moving the cursor position.

function erase_line()

    print("\e[2K")

end

# Move the cursor to the previous row, without changing the column position,
# and erase its line.

function erase_previous_line()

    cursor_up()
    erase_line()

end

# Prompt the user to enter a command and return it.

function command(prompt = "> ")::String

    print(prompt)
    return readline()

end

# Print the given prompt and wait until the user enters an empty string.

function press_enter(prompt::String)

    while ! (command(prompt) == "")
    end

end

# Clear the screen, display the credits and wait for the Enter key to be
# pressed.

function print_credits()

    clear_screen()
    println("Bug\n")
    println("Original version in BASIC:")
    println("    Brian Leibowitz, 1978.")
    println("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    println("This version in Julia:")
    println("    Copyright (c) 2024, Marcos Cruz (programandala.net)")
    println("    SPDX-License-Identifier: Fair\n")
    press_enter("Press Enter to read the instructions. ")

end

const COLUMNS           = 3
const COLUMN_WIDTH      = 8
const COLUMN_SEPARATION = 2

# Print a table with the bug parts' description.

function print_parts_table()

    # Headers
    header = ["Number", "Part", "Quantity"]
    for i in 1 : COLUMNS
        print(rpad(header[i], COLUMN_WIDTH + COLUMN_SEPARATION))
    end
    println()

    # Rulers
    for i in 1 : COLUMNS
        print(repeat("-", COLUMN_WIDTH), i == COLUMNS ? "" : repeat(' ', COLUMN_SEPARATION))
    end
    println()

    # Data
    for n in FIRST_PART : LAST_PART
        println(
            rpad(n, COLUMN_WIDTH + COLUMN_SEPARATION),
            rpad(uppercasefirst(PART_NAME[n]), COLUMN_WIDTH + COLUMN_SEPARATION),
            PART_QUANTITY[n])
    end

end

# Clear the screen, print the instructions and wait for a keypress.

function print_instructions()

    clear_screen()
    println("Bug")
    println("""

        The object is to finish your bug before I finish mine. Each number
        stands for a part of the bug body.

        I will roll the die for you, tell you what I rolled for you, what the
        number stands for, and if you can get the part. If you can get the
        part I will give it to you. The same will happen on my turn.

        If there is a change in either bug I will give you the option of
        seeing the pictures of the bugs. The numbers stand for parts as
        follows:
        """
        )
    print_parts_table()
    press_enter("\nPress Enter to start. ")

end

# Print a bug head.

function print_head()

    println("        HHHHHHH")
    println("        H     H")
    println("        H O O H")
    println("        H     H")
    println("        H  V  H")
    println("        HHHHHHH")

end

# Print the given bug.

function print_bug(bug::Bug)

    if bug.feelers > 0
        for i in 0 : FEELER_LENGTH - 1
            print("        ")
            for j in 0 : bug.feelers - 1
                print(" ", bug.feeler_type)
            end
            println()
        end
    end
    if bug.head
        print_head()
    end
    if bug.neck
        for i in 0 : NECK_LENGTH - 1
            println("          N N")
        end
    end
    if bug.body
        println("     BBBBBBBBBBBB")
        for i in 0 : BODY_HEIGHT - 1
            println("     B          B")
        end
        if bug.tail
            println("TTTTTB          B")
        end
        println("     BBBBBBBBBBBB")
    end
    if bug.legs > 0
        for i in 0 : LEG_LENGTH - 1
            print("    ")
            for j in 0 : bug.legs - 1
                print(" L")
            end
            println()
        end
    end

end

# Return `true` if the given bug is finished; otherwise return `false`.

function is_finished(bug::Bug)::Bool

    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS

end

# Array to convert a number to its equilavent text.

const AS_TEXT = ["a", "two", "three", "four", "five", "six"]

# Return a string containing the given number and noun in their proper form.

function plural(number::Int, noun::String)::String

    return AS_TEXT[number] * " " * noun * (number > 1 ? "s" : "")

end

# Add the given part to the given player's bug.

function add_part(part::Int, player::Player)::Bool

    changed = false
    if part == Int(body)
        if player.bug.body
            print(", but ", player.pronoun, " already have a body.\n")
        else
            print("; ", player.pronoun, " now have a body:\n")
            player.bug.body = true
            changed = true
        end
    elseif part == Int(neck)
        if player.bug.neck
            print(", but ", player.pronoun, " already have a neck.\n")
        elseif ! player.bug.body
            print(", but ", player.pronoun, " need a body first.\n")
        else
            print("; ", player.pronoun, " now have a neck:\n")
            player.bug.neck = true
            changed = true
        end
    elseif part == Int(head)
        if player.bug.head
            print(", but ", player.pronoun, " already have a head.\n")
        elseif ! player.bug.neck
            print(", but ", player.pronoun, " need a a neck first.\n")
        else
            print("; ", player.pronoun, " now have a head:\n")
            player.bug.head = true
            changed = true
        end
    elseif part == Int(feeler)
        if player.bug.feelers == MAX_FEELERS
            print(", but ", player.pronoun, " have two feelers already.\n")
        elseif ! player.bug.head
            print(", but ", player.pronoun, " need a head first.\n")
        else
            player.bug.feelers += 1
            print("; ", player.pronoun, " now have ", plural(player.bug.feelers, "feeler"))
            println(":")
            changed = true
        end
    elseif part == Int(tail)
        if player.bug.tail
            print(", but ", player.pronoun, " already have a tail.\n")
        elseif ! player.bug.body
            print(", but ", player.pronoun, " need a body first.\n")
        else
            print("; ", player.pronoun, " now have a tail:\n")
            player.bug.tail = true
            changed = true
        end
    elseif part == Int(leg)
        if player.bug.legs == MAX_LEGS
            print(", but ", player.pronoun, " have ", AS_TEXT[MAX_LEGS], " feet already.")
        elseif ! player.bug.body
            print(", but ", player.pronoun, " need a body first.\n")
        else
            player.bug.legs += 1
            print("; ", player.pronoun, " now have ", plural(player.bug.legs, "leg"))
            println(":")
            changed = true
        end
    end
    return changed

end

# Ask the user to press the Enter key, wait for the input, then erase the
# prompt text.

function prompt()

    press_enter("Press Enter to roll the dice. ")
    erase_previous_line()

end

# Play one turn for the given player, rolling the dice and updating his bug.

function turn(player::Player)

    prompt()
    part = rand(FIRST_PART : LAST_PART) # dice
    print("$(uppercasefirst(player.pronoun)) rolled a $part ($(PART_NAME[part]))")
    if add_part(part, player)
        println()
        print_bug(player.bug)
    end
    println()

end

# Print a message about the winner.

function print_winner(p1, p2::Player)

    if is_finished(p1.bug) && is_finished(p2.bug)
        println("Both of our bugs are finished in the same number of turns!")
    elseif is_finished(p1.bug)
        print(p1.possessive, " bug is finished.\n")
    elseif is_finished(p2.bug)
        print(p2.possessive, " bug is finished.\n")
    end

end

# Return a new player initialized with the given data.

function new_player(pronoun::String, possessive::String, feeler_type::Char)

    bug = Bug(false, false, false, 0, feeler_type, false, 0)
    return Player(pronoun, possessive, bug)

end

# Execute the game loop.

function play()

    clear_screen()
    computer = new_player("you", "your", 'A')
    human = new_player("I", "My", 'F')
    while ! (is_finished(human.bug) || is_finished(computer.bug))
        turn(human)
        turn(computer)
    end
    print_winner(human, computer)

end

print_credits()
print_instructions()
play()
println("I hope you enjoyed the game, play it again soon!!")

En Kotlin

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Kotlin:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2023-10-26.

Last modified: 20250518T2359+0200.
*/


class Bug (
    var body: Boolean,
    var neck: Boolean,
    var head: Boolean,
    var feelers: Int,
    val feelerType: Char,
    var tail: Boolean,
    var legs: Int,
)

class Player (
    val pronoun: String,
    val possessive: String,
    val bug: Bug,
)

val computer = Player("I", "My", Bug(false, false, false, 0, 'F', false, 0))

val human = Player("you", "Your", Bug(false, false, false, 0, 'A', false, 0))

enum class Part {Body, Neck, Head, Feeler, Tail, Leg}

// Bug body attributes.
const val BODY_HEIGHT = 2
const val FEELER_LENGTH = 4
const val LEG_LENGTH = 2
const val MAX_FEELERS = 2
const val MAX_LEGS = 6
const val NECK_LENGTH = 2

// Move the cursor to the home position.
fun home() {
    print("\u001B[H")
}

// Clear the screen and move the cursor to the home position.
fun clear() {
    print("\u001B[2J")
    home()
}

// Move the cursor up by the given number of rows (defaults to 1), without
// changing the column position.
fun cursorUp(rows: Short = 1) {
    print("\u001B[${rows}A")
}

// Erase the current line, without moving the cursor position.
fun eraseLine() {
    print("\u001B[2K")
}

// Move the cursor to the previous row, without changing the column position,
// and erase its line.
fun erasePreviousLine() {
    cursorUp()
    eraseLine()
}

// Print the given prompt and wait until the user presses the Enter key.
fun pressEnter(prompt: String) {
    print(prompt)
    readln()
}

// Clear the screen, display the credits and wait for a keypress.
fun printCredits() {
    clear()
    println("Bug\n")
    println("Original version in BASIC:")
    println("    Brian Leibowitz, 1978.")
    println("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    println("This version in Kotlin:")
    println("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    println("    SPDX-License-Identifier: Fair\n")
    pressEnter("Press Enter to read the instructions. ")
}

const val instructions = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
"""

// Print a table with the bug parts' description.
fun printPartsTable() {

    val COLUMNS = 3
    val COLUMN_WIDTH = 8
    val COLUMN_SEPARATION = 2

    // Headers
    val header = arrayOf<String>("Number", "Part", "Quantity")
    for (i in 0..<COLUMNS) {
        print(header[i].padEnd(COLUMN_WIDTH + COLUMN_SEPARATION))
    }
    println()

    // Rulers
    for (i in 0..<COLUMNS) {
        print("-".repeat(COLUMN_WIDTH))
        print(if (i == COLUMNS - 1) "" else " ".repeat(COLUMN_SEPARATION))
    }
    println()

    // Data
    val partQuantity = arrayOf(1, 1, 1, 2, 1, 6)
    for (part in enumValues<Part>()) {
        print((part.ordinal + 1).toString().padEnd(COLUMN_WIDTH + COLUMN_SEPARATION))
        print(part.toString().padEnd(COLUMN_WIDTH + COLUMN_SEPARATION))
        println(partQuantity[part.ordinal])
    }

}

// Clear the screen, print the instructions and wait for a keypress.
fun printInstructions() {
    clear()
    println("Bug")
    println(instructions)
    printPartsTable()
    pressEnter("\nPress Enter to start. ")
}

// Print a bug head.
fun printHead() {
    println("        HHHHHHH")
    println("        H     H")
    println("        H O O H")
    println("        H     H")
    println("        H  V  H")
    println("        HHHHHHH")
}

// Print the given bug.
fun printBug(bug: Bug) {
    if (bug.feelers > 0) {
        for (i in 0..<FEELER_LENGTH) {
            print("        ")
            for (j in 0..<bug.feelers) {
                print(" ${bug.feelerType}")
            }
            println()
        }
    }
    if (bug.head) {
        printHead()
    }
    if (bug.neck) {
        for (i in 0..<NECK_LENGTH) {
            println("          N N")
        }
    }
    if (bug.body) {
        println("     BBBBBBBBBBBB")
        for (i in 0..<BODY_HEIGHT) {
            println("     B          B")
        }
        if (bug.tail) {
            println("TTTTTB          B")
        }
        println("     BBBBBBBBBBBB")
    }
    if (bug.legs > 0) {
        for (i in 0..<LEG_LENGTH) {
            print("    ")
            for (j in 0..<bug.legs) {
                print(" L")
            }
            println()
        }
    }
}

// Return `true` if the given bug is finished; otherwise return `false`.
fun finished(bug: Bug): Boolean {
    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS
}

// Array to convert a number to its equilavent text.
val asText = arrayOf<String>(
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six") // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
fun plural(number: Int, noun: String): String {
    return "${asText[number]} $noun${if (number > 1) "s" else ""}"
}

// Add the given part to the given player's bug, if needed; return `true` if the
// bug was changed, otherwise return `false`.
fun addPart(part: Part, player: Player): Boolean {
    when (part) {
        Part.Body ->
            if (player.bug.body) {
                println(", but ${player.pronoun} already have a body.")
            } else {
                player.bug.body = true
                println("; ${player.pronoun} now have a body:")
                return true
            }
        Part.Neck ->
            if (player.bug.neck) {
                println(", but ${player.pronoun} you already have a neck.")
            } else if (!player.bug.body) {
                println(", but ${player.pronoun} need a body first.")
            } else {
                player.bug.neck = true
                println("; ${player.pronoun} now have a neck:")
                return true
            }
        Part.Head ->
            if (player.bug.head) {
                println(", but ${player.pronoun} already have a head.")
            } else if (!player.bug.neck) {
                println(", but ${player.pronoun} need a a neck first.")
            } else {
                player.bug.head = true
                println("; ${player.pronoun} now have a head:")
                return true
            }
        Part.Feeler ->
            if (player.bug.feelers == MAX_FEELERS) {
                println(", but ${player.pronoun} have two feelers already.")
            } else if (!player.bug.head) {
                println(", but ${player.pronoun} need a head first.")
            } else {
                player.bug.feelers += 1
                println("; ${player.pronoun} now have ${plural(player.bug.feelers, "feeler")}:")
                return true
            }
        Part.Tail ->
            if (player.bug.tail) {
                println(", but ${player.pronoun} already have a tail.")
            } else if (!player.bug.body) {
                println(", but ${player.pronoun} need a body first.")
            } else {
                player.bug.tail = true
                println("; ${player.pronoun} now have a tail:")
                return true
            }
        Part.Leg ->
            if (player.bug.legs == MAX_LEGS) {
                println(", but ${player.pronoun} have ${asText[MAX_LEGS]} feet already.")
            } else if (!player.bug.body) {
                println(", but ${player.pronoun} need a body first.")
            } else {
                player.bug.legs += 1
                println("; ${player.pronoun} now have ${plural(player.bug.legs, "leg")}:")
                return true
            }
    }
    return false
}

// Ask the user to press the Enter key, wait for the input, then erase the
// prompt text.
fun prompt() {
    pressEnter("Press Enter to roll the dice. ")
    erasePreviousLine()
}

// Play one turn for the given player, rolling the dice and updating his bug.
fun turn(player: Player) {
    prompt()
    val number: Int = (0..<enumValues<Part>().size).random()
    val part : Part = enumValues<Part>()[number]
    print(player.pronoun.lowercase().replaceFirstChar{it.uppercase()})
    print(" rolled a ${number + 1} (${part.toString().lowercase()})")
    if (addPart(part, player)) {
        println()
        printBug(player.bug)
    }
    println()
}

// Print a message about the winner.
fun printWinner() {
    if (finished(human.bug) && finished(computer.bug)) {
        println("Both of our bugs are finished in the same number of turns!")
    } else if (finished(human.bug)) {
        println("${human.possessive} bug is finished.")
    } else if (finished(computer.bug)) {
        println("${computer.possessive} bug is finished.")
    }
}

// Return `true` if either bug is finished, i.e. the game ending condition.
fun gameOver(): Boolean {
    return finished(human.bug) || finished(computer.bug)
}

// Execute the game loop.
fun play() {
    clear()
    while (!gameOver()) {
        turn(human)
        turn(computer)
    }
    printWinner()
}

fun main() {
    printCredits()
    printInstructions()
    play()
    println("I hope you enjoyed the game, play it again soon!!")
}

En Nim

#[
Bug

Original version in BASIC:
  Brian Leibowitz, 1978.
  Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Nim:
  Copyright (c) 2025, Marcos Cruz (programandala.net)
  SPDX-License-Identifier: Fair

Written on 2025-01-20.

Last modified: 20250405T0304+0200.
]#

import std/random
import std/strformat
import std/strutils
import std/terminal
import std/unicode

type Bug = tuple [
  body: bool,
  neck: bool,
  head: bool,
  feelers: int,
  feelerType: char,
  tail: bool,
  legs: int ]

type Player = tuple [
  pronoun: string,
  possessive: string,
  bug: Bug ]

var computer: Player
var human: Player

const bugParts = 6

type Part = enum
    body = 1, neck, head, feeler, tail, leg

# Bug body attributes.
#
const bodyHeight = 2
const feelerLength = 4
const legLength = 2
const maxFeelers = 2
const maxLegs = 6
const neckLength = 2

proc cursorHome() =
  setCursorPos(0, 0)

proc clearScreen() =
  eraseScreen()
  cursorHome()

# Move the cursor to the previous row, without changing the column position,
# and erase its line.
#
proc erasePreviousLine() =
  cursorUp()
  eraseLine()

# Clear the screen, display the credits and wait for a keypress.
#
proc printCredits() =
  clearScreen()
  writeLine(stdout, "Bug\n")
  writeLine(stdout, "Original version in BASIC:")
  writeLine(stdout, "    Brian Leibowitz, 1978.")
  writeLine(stdout, "    Creative computing (Morristown, New Jersey, USA), 1978.\n")
  writeLine(stdout, "This version in Nim:")
  writeLine(stdout, "    Copyright (c) 2025, Marcos Cruz (programandala.net)")
  writeLine(stdout, "    SPDX-License-Identifier: Fair\n")
  write(stdout, "Press Enter to read the instructions. ")
  discard readLine(stdin)

const instructions = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
"""

# Print a table with the bug parts' description.
#
proc printPartsTable() =
  const columns = 3
  const columnWidth = 8
  const columnSeparation = 2
  var padding: string

  # Headers
  const header: array[3, string] = ["Number", "Part", "Quantity"]
  for i in 0 ..< columns:
    padding = repeat(" ", columnWidth + columnSeparation - len(header[i]))
    write(stdout, header[i], padding)
  writeLine(stdout, "")

  # Rulers
  const ruler = repeat("-", columnWidth)
  for i in 0 ..< columns:
    padding = if i == columns - 1: "" else: repeat(" ", columnSeparation)
    write(stdout, ruler, padding)
  writeLine(stdout, "")

  # Data
  var partQuantity: array[bugParts, int] = [1, 1, 1, 2, 1, 6]
  for part in Part:
    padding = repeat(" ", columnWidth + columnSeparation - len($int(part)))
    write(stdout, int(part), padding)
    padding = repeat(" ", columnWidth + columnSeparation - len($part))
    write(stdout, capitalize($part), padding)
    writeLine(stdout, partQuantity[int(part) - 1])

proc printInstructions() =
  clearScreen()
  writeLine(stdout, "Bug\n")
  writeLine(stdout, instructions)
  printPartsTable()
  write(stdout, "\nPress Enter to start. ")
  discard readLine(stdin)

proc printHead() =
  writeLine(stdout, "        HHHHHHH")
  writeLine(stdout, "        H     H")
  writeLine(stdout, "        H O O H")
  writeLine(stdout, "        H     H")
  writeLine(stdout, "        H  V  H")
  writeLine(stdout, "        HHHHHHH")

proc printBug(bug: Bug) =
  if bug.feelers > 0:
    for _ in 0 ..< feelerLength:
      write(stdout, "        ")
      for _ in 0 ..< bug.feelers:
        write(stdout, " ", bug.feelerType)
      writeLine(stdout, "")
  if bug.head:
    printHead()
  if bug.neck:
    for _ in 0 ..< neckLength:
      writeLine(stdout, "          N N")
  if bug.body:
    writeLine(stdout, "     BBBBBBBBBBBB")
    for _ in 0 ..< bodyHeight:
      writeLine(stdout, "     B          B")
    if bug.tail:
      writeLine(stdout, "TTTTTB          B")
    writeLine(stdout, "     BBBBBBBBBBBB")
  if bug.legs > 0:
    for _ in 0 ..< legLength:
      write(stdout, "    ")
      for _ in 0 ..< bug.legs:
        write(stdout, " L")
      writeLine(stdout, "")

proc finished(bug: Bug): bool  =
  return bug.feelers == maxFeelers and bug.tail and bug.legs == maxLegs

proc dice(): int  =
  return rand(1 .. 6)

const asText: array[maxLegs + 1, string] = [
  "no",
  "a",
  "two",
  "three",
  "four",
  "five",
  "six" ]

# Return a string containing the given number and noun in their proper form.
#
proc plural(number: int, noun: string): string  =
  let ending = if number > 1: "s" else: ""
  return fmt" {asText[number]} {noun}{ending}"

# Add the given part to the given player's bug.
#
proc addPart(part: var Part, player: var Player): bool  =
  var changed: bool = false
  case part:
  of Part.body:
    if player.bug.body:
      writeLine(stdout, ", but ", player.pronoun, " already have a body.")
    else:
      writeLine(stdout, "; ", player.pronoun, " now have a body:")
      player.bug.body = true
      changed = true
  of Part.neck:
    if player.bug.neck:
      writeLine(stdout, ", but ", player.pronoun, " already have a neck.")
    elif not player.bug.body:
      writeLine(stdout, ", but ", player.pronoun, " need a body first.")
    else:
      writeLine(stdout, "; ", player.pronoun, " now have a neck:")
      player.bug.neck = true
      changed = true
  of Part.head:
    if player.bug.head:
      writeLine(stdout, ", but ", player.pronoun, " already have a head.")
    elif not player.bug.neck:
      writeLine(stdout, ", but ", player.pronoun, " need a a neck first.")
    else:
      writeLine(stdout, "; ", player.pronoun, " now have a head:")
      player.bug.head = true
      changed = true
  of Part.feeler:
    if player.bug.feelers == maxFeelers:
      writeLine(stdout, ", but ", player.pronoun, " have two feelers already.")
    elif not player.bug.head:
      writeLine(stdout, ", but ", player.pronoun, " need a head first.")
    else:
      player.bug.feelers += 1
      write(stdout, "; ", player.pronoun, " now have",
        plural(player.bug.feelers, "feeler"))
      writeLine(stdout, ":")
      changed = true
  of Part.tail:
    if player.bug.tail:
      writeLine(stdout, ", but ", player.pronoun, " already have a tail.")
    elif not player.bug.body:
      writeLine(stdout, ", but ", player.pronoun, " need a body first.")
    else:
      writeLine(stdout, "; ", player.pronoun, " now have a tail:")
      player.bug.tail = true
      changed = true
  of Part.leg:
    if player.bug.legs == maxLegs:
      writeLine(stdout, ", but ", player.pronoun, " have",
        asText[maxLegs], "feet already.")
    elif not player.bug.body:
      writeLine(stdout, ", but ", player.pronoun, " need a body first.")
    else:
      player.bug.legs += 1
      write(stdout, "; ", player.pronoun, " now have",
        plural(player.bug.legs, "leg"))
      writeLine(stdout, ":")
      changed = true
  return changed

# Ask the user to press the Enter key, wait for the input, then erase the
# prompt text.
#
proc prompt() =
  write(stdout, "Press Enter to roll the dice. ")
  discard readLine(stdin)
  erasePreviousLine()

# Play one turn for the given player, rolling the dice and updating his bug.
#
proc turn(player: var Player) =
  prompt()
  var number: int = dice()
  var part = Part(number)
  write(stdout, capitalize(player.pronoun), " rolled a ", number, " (", part, ")")
  if addPart(part, player):
    writeLine(stdout, "")
    printBug(player.bug)
  writeLine(stdout, "")

proc printWinner() =
  if finished(human.bug) and finished(computer.bug):
    writeLine(stdout, "Both of our bugs are finished in the same number of turns!")
  elif finished(human.bug):
    writeLine(stdout, human.possessive, " bug is finished.")
  elif finished(computer.bug):
    writeLine(stdout, computer.possessive, " bug is finished.")

proc gameOver(): bool  =
  return finished(human.bug) or finished(computer.bug)

proc play() =
  clearScreen()
  while not gameOver():
    turn(human)
    turn(computer)
  printWinner()

# Init the players' data before a new game.
#
proc init() =
  human.pronoun = "you"
  human.possessive = "Your"
  human.bug.feelerType = 'A'
  computer.pronoun = "I"
  computer.possessive = "My"
  computer.bug.feelerType = 'F'

init()
printCredits()
printInstructions()
play()
writeLine(stdout, "I hope you enjoyed the game, play it again soon!!")

En Odin

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Odin:
    Copyright (c) 2023, 2024, 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written in 2023-03, 2023-09, 2023-10, 2023-12, 2024-05, 2024-07, 2024-12, 2025-02.

Last modified: 20250227T1849+0100.
*/

package bug

import "../lib/anodino/src/read"
import "../lib/anodino/src/str"
import "../lib/anodino/src/term"
import "core:fmt"
import "core:math/rand"
import "core:strings"

// Bug type.
//
Bug :: struct {

    body : bool,
    neck : bool,
    head : bool,
    feelers : int,
    feeler_type : rune,
    tail : bool,
    legs : int,

}

// Player type.
//
Player :: struct {

    pronoun : string,
    possessive : string,
    bug : Bug,

}

// Players.
//
computer, human : Player

// Bug body parts.
//
Part :: enum {body = 1, neck, head, feeler, tail, leg}

// Bug body attributes.
//
BODY_HEIGHT :: 2
FEELER_LENGTH :: 4
LEG_LENGTH :: 2
MAX_FEELERS :: 2
MAX_LEGS :: 6
NECK_LENGTH :: 2

// Move the cursor to the previous row, without changing the column position,
// and erase its line.
//
erase_previous_line :: proc() {

    term.move_cursor_up()
    term.erase_line()

}

// Clear the screen, display the credits and wait for a keypress.
//
print_credits :: proc() {

    term.clear_screen()
    fmt.println("Bug\n")
    fmt.println("Original version in BASIC:")
    fmt.println("    Brian Leibowitz, 1978.")
    fmt.println("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    fmt.println("This version in Odin:")
    fmt.println("    Copyright (c) 2023, 2024, 2025, Marcos Cruz (programandala.net)")
    fmt.println("    SPDX-License-Identifier: Fair\n")
    s, _ := read.a_prompted_string("Press Enter to read the instructions. ")
    delete(s)

}

INSTRUCTIONS :: `
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
`

// Print a table with the bug parts' description.
//
print_parts_table :: proc() {

    COLUMNS :: 3
    COLUMN_WIDTH :: 8
    COLUMN_SEPARATION :: 2

    // Headers
    header := []string{"Number", "Part", "Quantity"}
    for i in 0 ..< COLUMNS {
        justified_header := strings.left_justify(header[i], COLUMN_WIDTH + COLUMN_SEPARATION, " ")
        defer delete(justified_header)
        fmt.print(justified_header)
    }
    fmt.println()

    // Rulers
    ruler := strings.repeat("-", COLUMN_WIDTH)
    defer delete(ruler)
    padding := strings.repeat(" ", COLUMN_SEPARATION)
    defer delete(padding)
    for i in 0 ..< COLUMNS {
        fmt.print(ruler, i == COLUMNS - 1 ? "" : padding, sep = "")
    }
    fmt.println()

    // Data
    part_quantity : [len(Part) + 1]int
    part_quantity[Part.body] = 1
    part_quantity[Part.neck] = 1
    part_quantity[Part.head] = 1
    part_quantity[Part.feeler] = 2
    part_quantity[Part.tail] = 1
    part_quantity[Part.leg] = 6
    for part in Part {
        part_number := fmt.tprint(int(part))
        justified_part_number := strings.left_justify(part_number, COLUMN_WIDTH + COLUMN_SEPARATION, " ")
        defer delete(justified_part_number)
        part_name := fmt.tprint(part)
        capital_part_name := str.to_capital(part_name)
        defer delete(capital_part_name)
        justified_part_name := strings.left_justify(capital_part_name, COLUMN_WIDTH + COLUMN_SEPARATION, " ")
        defer delete(justified_part_name)
        fmt.println(justified_part_number, justified_part_name, part_quantity[part], sep = "")
    }

}

// Clear the screen, print the instructions and wait for a keypress.
//
print_instructions :: proc() {

    term.clear_screen()
    fmt.println("Bug")
    fmt.println(INSTRUCTIONS)
    print_parts_table()
    s, _ := read.a_prompted_string("\nPress Enter to start. ")
    delete(s)

}

// Print a bug head.
//
print_head :: proc() {

    fmt.println("        HHHHHHH")
    fmt.println("        H     H")
    fmt.println("        H O O H")
    fmt.println("        H     H")
    fmt.println("        H  V  H")
    fmt.println("        HHHHHHH")

}

// Print the given bug.
//
print_bug :: proc(bug : Bug) {

    if bug.feelers > 0 {
        for _ in 0 ..< FEELER_LENGTH {
            fmt.print("        ")
            for _ in 0 ..< bug.feelers {
                fmt.print(" ", bug.feeler_type)
            }
            fmt.println()
        }
    }
    if bug.head {
        print_head()
    }
    if bug.neck {
        for _ in 0 ..< NECK_LENGTH {
            fmt.println("          N N")
        }
    }
    if bug.body {
        fmt.println("     BBBBBBBBBBBB")
        for _ in 0 ..< BODY_HEIGHT {
            fmt.println("     B          B")
        }
        if bug.tail {
            fmt.println("TTTTTB          B")
        }
        fmt.println("     BBBBBBBBBBBB")
    }
    if bug.legs > 0 {
        for _ in 0 ..< LEG_LENGTH {
            fmt.print("    ")
            for _ in 0 ..< bug.legs {
                fmt.print(" L")
            }
            fmt.println()
        }
    }

}

// Return `true` if the given bug is finished; otherwise return `false`.
//
finished :: proc(bug : Bug) -> bool {

    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS

}

// Return a random number from 1 to 6 (inclusive).
//
dice :: proc() -> int {

    return rand.int_max(6) + 1

}

// Array to convert a number to its equilavent text.
//
as_text := []string{
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" } // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
//
plural :: proc(number : int, noun : string) -> string {

    return fmt.tprint(as_text[number], " ", noun, (number > 1) ? "s" : "", sep = "")

}

// Add the given part to the given player's bug.
//
add_part :: proc(part : Part, player : ^Player) -> bool {

    changed : bool = false
    #partial switch (part) {
    case Part.body:
        if player^.bug.body {
            fmt.println(", but", player^.pronoun, "already have a body.")
        } else {
            fmt.println(";", player^.pronoun, "now have a body:")
            player^.bug.body = true
            changed = true
        }
    case Part.neck:
        if player^.bug.neck {
            fmt.println(", but", player^.pronoun, "already have a neck.")
        } else if !player^.bug.body {
            fmt.println(", but", player^.pronoun, "need a body first.")
        } else {
            fmt.println(";", player^.pronoun, "now have a neck:")
            player^.bug.neck = true
            changed = true
        }
    case Part.head:
        if player^.bug.head {
            fmt.println(", but", player^.pronoun, "already have a head.")
        } else if !player^.bug.neck {
            fmt.println(", but", player^.pronoun, "need a a neck first.")
        } else {
            fmt.println(";", player^.pronoun, "now have a head:")
            player^.bug.head = true
            changed = true
        }
    case Part.feeler:
        if player^.bug.feelers == MAX_FEELERS {
            fmt.println(", but", player^.pronoun, "have two feelers already.")
        } else if !player^.bug.head {
            fmt.println(", but", player^.pronoun, "need a head first.")
        } else {
            player^.bug.feelers += 1
            fmt.print(";", player^.pronoun, "now have",
                plural(player^.bug.feelers, "feeler"))
            fmt.println(":")
            changed = true
        }
    case Part.tail:
        if player^.bug.tail {
            fmt.println(", but", player^.pronoun, "already have a tail.")
        } else if !player^.bug.body {
            fmt.println(", but", player^.pronoun, "need a body first.")
        } else {
            fmt.println(";", player^.pronoun, "now have a tail:")
            player^.bug.tail = true
            changed = true
        }
    case Part.leg:
        if player^.bug.legs == MAX_LEGS {
            fmt.println(", but", player^.pronoun, "have",
                as_text[MAX_LEGS], "feet already.")
        } else if !player^.bug.body {
            fmt.println(", but", player^.pronoun, "need a body first.")
        } else {
            player^.bug.legs += 1
            fmt.print(";", player^.pronoun, "now have",
                plural(player^.bug.legs, "leg"))
            fmt.println(":")
            changed = true
        }
    }
    return changed

}

// Ask the user to press the Enter key, wait for the input, then erase the
// prompt text.
//
prompt :: proc() {

    s, _ := read.a_prompted_string("Press Enter to roll the dice. ")
    delete(s)
    erase_previous_line()

}

// Play one turn for the given player, rolling the dice and updating his bug.
//
turn :: proc(player : ^Player) {

    prompt()
    number : int = dice()
    part := Part(number)
    pronoun := str.to_capital(player^.pronoun)
    defer delete(pronoun)
    fmt.printf("%s rolled a %i (%s)", pronoun, number, part)
    if add_part(part, player) {
        fmt.println()
        print_bug(player^.bug)
    }
    fmt.println()

}

// Print a message about the winner.
//
print_winner :: proc() {

    if finished(human.bug) && finished(computer.bug) {
        fmt.println("Both of our bugs are finished in the same number of turns!")
    } else if finished(human.bug) {
        fmt.println(human.possessive, "bug is finished.")
    } else if finished(computer.bug) {
        fmt.println(computer.possessive, "bug is finished.")
    }

}

// Return `true` if either bug is finished, i.e. the game ending condition.
//
game_over :: proc() -> bool {

    return finished(human.bug) || finished(computer.bug)

}

// Execute the game loop.
//
play :: proc() {

    term.clear_screen()
    for !game_over() {
        turn(&human)
        turn(&computer)
    }
    print_winner()

}

// Init the players' data before a new game.
//
init :: proc() {

    human.pronoun = "you"
    human.possessive = "Your"
    human.bug.feeler_type = 'A'
    computer.pronoun = "I"
    computer.possessive = "My"
    computer.bug.feeler_type = 'F'

}

main :: proc() {

    init()
    print_credits()
    print_instructions()
    play()
    fmt.println("I hope you enjoyed the game, play it again soon!!")

}

En Pike

#!/usr/bin/env pike

// Bug

// Original version in BASIC:
//  Brian Leibowitz, 1978.
//  Creative Computing (Morristown, New Jersey, USA), 1978.

// This version in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2025-03-10.
//
// Last modified: 20250312T1154+0100.

// Data {{{1
// =============================================================================

class Bug {
    bool body;
    bool neck;
    bool head;
    int feelers;
    string feeler_type;
    bool tail;
    int legs;
}

class Player {
    string pronoun;
    string possessive;
    Bug bug;
}

Player computer = Player();
Player human = Player();

void init_data() {

    computer.pronoun = "I";
    computer.possessive = "My";
    computer.bug = Bug();
    computer.bug.body = false;
    computer.bug.neck = false;
    computer.bug.head = false;
    computer.bug.feelers = 0;
    computer.bug.feeler_type = "F";
    computer.bug.tail = false;
    computer.bug.legs = 0;

    human.pronoun = "you";
    human.possessive = "Your";
    human.bug = Bug();
    human.bug.body = false;
    human.bug.neck = false;
    human.bug.head = false;
    human.bug.feelers = 0;
    human.bug.feeler_type = "A";
    human.bug.tail = false;
    human.bug.legs = 0;
}

enum Bug_part {
    BODY = 1,
    NECK,
    HEAD,
    FEELER,
    TAIL,
    LEG,
}

constant FIRST_PART = BODY;
constant LAST_PART = LEG;

int part_quantity(Bug_part part) {
    switch (part) {
        case BODY: return 1;
        case NECK: return 1;
        case HEAD: return 1;
        case FEELER: return 2;
        case TAIL: return 1;
        case LEG: return 6;
    }
}

string part_name(Bug_part part) {
    switch (part) {
        case BODY: return "body";
        case NECK: return "neck";
        case HEAD: return "head";
        case FEELER: return "feeler";
        case TAIL: return "tail";
        case LEG: return "leg";
    }
}

// Bug body attributes.
//
constant BODY_HEIGHT = 2;
constant FEELER_LENGTH = 4;
constant LEG_LENGTH = 2;
constant MAX_FEELERS = 2;
constant MAX_LEGS = 6;
constant NECK_LENGTH = 2;

// Terminal {{{1
// =============================================================================

constant NORMAL_STYLE = 0;

void move_cursor_home() {
    write("\x1B[H");
}

void move_cursor_up() {
    write("\x1B[1A");
}

void set_style(int style) {
    write("\x1B[%dm", style);
}

void reset_attributes() {
    set_style(NORMAL_STYLE);
}

void erase_screen() {
    write("\x1B[2J");
}

void clear_screen() {
    erase_screen();
    reset_attributes();
    move_cursor_home();
}

// Erase the entire current line and move the cursor to the start of the line.
//
void erase_line() {
    write("\x1B[2K");
}

// User input {{{1
// =============================================================================

string accept_string(string prompt) {
    write(prompt);
    return Stdio.stdin.gets();
}

// Credits and instructions {{{1
// =============================================================================

// Clear the screen, display the credits and wait for a keypress.
//
void print_credits() {
    clear_screen();
    write("Bug\n\n");
    write("Original version in BASIC:\n");
    write("    Brian Leibowitz, 1978.\n");
    write("    Creative computing (Morristown, New Jersey, USA), 1978.\n\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n\n");
    accept_string("Press Enter to read the instructions. ");
}

constant INSTRUCTIONS =
"The object is to finish your bug before I finish mine. Each number\n" +
"stands for a part of the bug body.\n" +
"\n" +
"I will roll the die for you, tell you what I rolled for you, what the\n" +
"number stands for, and if you can get the part. If you can get the\n" +
"part I will give it to you. The same will happen on my turn.\n" +
"\n" +
"If there is a change in either bug I will give you the option of\n" +
"seeing the pictures of the bugs. The numbers stand for parts as\n" +
"follows:\n";

// Clear the screen, print the instructions and wait for a keypress.
//
void print_instructions() {
    clear_screen();
    write("Bug\n\n");
    write("%s\n", INSTRUCTIONS);
    print_parts_table();
    accept_string("\nPress Enter to start. ");
}

// Pseudo-random numbers {{{1
// =============================================================================

// Return a random number from 1 to 6 (inclusive).
//
int dice() {
    return (random(6) + 1);
}

// Main {{{1
// =============================================================================

// Print a table with the description of the bug parts.
//
void print_parts_table() {

    constant COLUMNS = 3;
    constant COLUMN_WIDTH = 8;
    constant COLUMN_SEPARATION = 2;

    string format_string = "%-" + (string) (COLUMN_WIDTH + COLUMN_SEPARATION) + "s";

    // Headers
    array(string) header = ({"Number", "Part", "Quantity"});
    for (int i = 0; i < COLUMNS; i += 1) {
        write(format_string, header[i]);
    }
    write("\n");

    // Rulers
    string ruler = "-" * COLUMN_WIDTH;
    string padding = " " * COLUMN_SEPARATION;
    for (int i = 0; i < COLUMNS; i += 1) {
        write("%s%s", ruler, i == COLUMNS - 1 ? "" : padding);
    }
    write("\n");

    // Data
    for (Bug_part part = FIRST_PART; part <= LAST_PART; part += 1) {
        write(format_string, (string) part);
        string name = String.capitalize(part_name(part));
        write(format_string, name);
        write(format_string, (string) part_quantity(part));
        write("\n");
    }
}

// Print a bug head.
//
void print_head() {
    write("        HHHHHHH\n");
    write("        H     H\n");
    write("        H O O H\n");
    write("        H     H\n");
    write("        H  V  H\n");
    write("        HHHHHHH\n");
}

// Print the given bug.
//
void print_bug(Bug bug) {
    if (bug.feelers > 0) {
        for (int i = 0; i < FEELER_LENGTH; i += 1) {
            write("        ");
            for (int i = 0; i < bug.feelers; i += 1) {
                write(" %s", bug.feeler_type);
            }
            write("\n");
        }
    }
    if (bug.head) {
        print_head();
    }
    if (bug.neck) {
        for (int i = 0; i < NECK_LENGTH; i += 1) {
            write("          N N\n");
        }
    }
    if (bug.body) {
        write("     BBBBBBBBBBBB\n");
        for (int i = 0; i < BODY_HEIGHT; i += 1) {
            write("     B          B\n");
        }
        if (bug.tail) {
            write("TTTTTB          B\n");
        }
        write("     BBBBBBBBBBBB\n");
    }
    if (bug.legs > 0) {
        for (int i = 0; i < LEG_LENGTH; i += 1) {
            write("    ");
            for (int i = 0; i < bug.legs; i += 1) {
                write(" L");
            }
            write("\n");
        }
    }
}

// Return `true` if the given bug is finished; otherwise return `false`.
//
bool finished(Bug bug) {
    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS;
}

// Array to convert a number to its equilavent text.
//
array(string) as_text = ({
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" }); // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
//
string plural(int number, string noun) {
    return as_text[number] + " " + noun + ((number > 1) ? "s" : "");
}

// Add the given part to the given player's bug.
//
bool add_part(Bug_part part, Player player) {
    bool changed = false;
    switch (part) {
        case BODY:
            if (player.bug.body) {
                write(", but %s already have a body.\n", player.pronoun);
            } else {
                write("; %s now have a body:\n", player.pronoun);
                player.bug.body = true;
                changed = true;
            }
            break;
        case NECK:
            if (player.bug.neck) {
                write(", but %s already have a neck.\n", player.pronoun);
            } else if (!player.bug.body) {
                write(", but %s need a body first.\n", player.pronoun);
            } else {
                write("; %s now have a neck:\n", player.pronoun);
                player.bug.neck = true;
                changed = true;
            }
            break;
        case HEAD:
            if (player.bug.head) {
                write(", but %s already have a head.\n", player.pronoun);
            } else if (!player.bug.neck) {
                write(", but %s need a a neck first.\n", player.pronoun);
            } else {
                write("; %s now have a head:\n", player.pronoun);
                player.bug.head = true;
                changed = true;
            }
            break;
        case FEELER:
            if (player.bug.feelers == MAX_FEELERS) {
                write(", but %s have two feelers already.\n", player.pronoun);
            } else if (!player.bug.head) {
                write(", but %s need a head first.\n", player.pronoun);
            } else {
                player.bug.feelers += 1;
                write("; %s now have %s", player.pronoun, plural(player.bug.feelers, "feeler"));
                write(":\n");
                changed = true;
            }
            break;
        case TAIL:
            if (player.bug.tail) {
                write(", but %s already have a tail.\n", player.pronoun);
            } else if (!player.bug.body) {
                write(", but %s need a body first.\n", player.pronoun);
            } else {
                write("; %s now have a tail:\n", player.pronoun);
                player.bug.tail = true;
                changed = true;
            }
            break;
        case LEG:
            if (player.bug.legs == MAX_LEGS) {
                write(", but %s have %s feet already.", player.pronoun, as_text[MAX_LEGS]);
            } else if (!player.bug.body) {
                write(", but %s need a body first.\n", player.pronoun);
            } else {
                player.bug.legs += 1;
                write("; %s now have %s", player.pronoun, plural(player.bug.legs, "leg"));
                write(":\n");
                changed = true;
            }
    }
    return changed;
}

// Play one turn for the given player, rolling the dice and updating his bug.
//
void turn(Player player) {
    accept_string("Press Enter to roll the dice. ");
    move_cursor_up();
    erase_line();
    int part = dice();
    string pronoun = String.capitalize(player.pronoun);
    write("%s rolled a %d (%s)", pronoun, part, part_name(part));
    if (add_part(part, player)) {
        write("\n");
        print_bug(player.bug);
    }
    write("\n");
}

// Print a message about the winner.
//
void print_winner() {
    if (finished(human.bug) && finished(computer.bug)) {
        write("Both of our bugs are finished in the same number of turns!\n");
    } else if (finished(human.bug)) {
        write("%s bug is finished.\n", human.possessive);
    } else if (finished(computer.bug)) {
        write("%s bug is finished.\n", computer.possessive);
    }
}

// Return `true` if either bug is finished, i.e. the game ending condition.
//
bool game_over() {
    return finished(human.bug) || finished(computer.bug);
}

// Execute the game loop.
//
void play() {
    clear_screen();
    while (!game_over()) {
        turn(human);
        turn(computer);
    }
    print_winner();
}

void bye() {
    write("I hope you enjoyed the game, play it again soon!!\n");
}

void main() {
    init_data();
    print_credits();
    print_instructions();
    play();
    bye();
}

En Raku

# Bug

# Original version in BASIC:
#   Brian Leibowitz, 1978.
#   Creative Computing (Morristown, New Jersey, USA), 1978.

# This version in Raku:
#   Copyright (c) 2024, Marcos Cruz (programandala.net)
#   SPDX-License-Identifier: Fair
#
# Written in 2024-12-11/12.
#
# Last modified: 20241212T1012+0100.

class Bug {

    has Bool $.body is rw = False;
    has Bool $.neck is rw = False;
    has Bool $.head is rw = False;
    has Int $.feelers is rw = 0;
    has Str $.feeler_type is rw = '';
    has Bool $.tail is rw = False;
    has Int $.legs is rw = 0;

}

class Player {

    has Str $.pronoun is rw = '';
    has Str $.possessive is rw = '';
    has Bug $.bug is rw = Bug.new();

}

my Player $computer = Player.new;
my Player $human = Player.new;

# Bug body parts.
#
constant $BODY = 0;
constant $NECK = 1;
constant $HEAD = 2;
constant $FEELER = 3;
constant $TAIL = 4;
constant $LEG = 5;
constant @PART_NAME = ('body', 'neck', 'head', 'feeler', 'tail', 'leg');
constant @PART_QUANTITY = (1, 1, 1, 2, 1, 6);

# Bug body attributes.
#
constant $BODY_HEIGHT = 2;
constant $FEELER_LENGTH = 4;
constant $LEG_LENGTH = 2;
constant $MAX_FEELERS = 2;
constant $MAX_LEGS = 6;
constant $NECK_LENGTH = 2;

# Move the cursor to the home position.
#
sub move_cursor_home {

    print "\e[H";

}

# Erase the screen.
#
sub erase_screen {

    print "\e[2J";

}

# Set the color.
#
sub set_color(Int $color) {

    print "\e[{$color}m";

}

# Reset the attributes.
#
sub reset_attributes {

    constant $RESET_ALL = 0;
    set_color($RESET_ALL);

}

# Erase the screen, reset the attributes and move the cursor to the home position.
sub clear_screen {

    erase_screen;
    reset_attributes;
    move_cursor_home;

}

# Move the cursor up the given number of positions (1 by default).
sub move_cursor_up(Int $n = 1) {

    print "\e[{$n}A";

}

# Erase the entire current line and move the cursor to the start of the line.
#
sub erase_line {

    print "\e[2K";

}

# Move the cursor to the previous row, without changing the column position,
# and erase its line.
#
sub erase_previous_line {

    move_cursor_up;
    erase_line;

}

# Clear the screen, display the credits and wait for a keypress.
#
sub print_credits {

    clear_screen;
    put "Bug\n";
    put 'Original version in BASIC:';
    put '    Brian Leibowitz, 1978.';
    put "    Creative computing (Morristown, New Jersey, USA), 1978.\n";
    put 'This version in Raku:';
    put '    Copyright (c) 2024, Marcos Cruz (programandala.net)';
    put "    SPDX-License-Identifier: Fair\n";
    prompt 'Press Enter to read the instructions. ';

}

constant $INSTRUCTIONS = '
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
';

# Return the given string padded with the given char at the right, up to the
# given width.
#
sub left_justify(Str $s, Int $width, Str $char --> Str) {

    return $s ~ (' ' x ($width - $s.chars));

}

# Print a table with the bug parts' description.
#
sub print_parts_table {

    constant $COLUMNS = 3;
    constant $COLUMN_WIDTH = 8;
    constant $COLUMN_SEPARATION = 2;

    # Headers
    my $header = ('Number', 'Part', 'Quantity');
    for 0 ..^ $COLUMNS -> $i {
        print left_justify($header[$i], $COLUMN_WIDTH + $COLUMN_SEPARATION, ' ');
    }
    put '';

    # Rulers
    for 0 ..^ $COLUMNS -> $i {
        print '-' x $COLUMN_WIDTH;
        print $i == $COLUMNS ?? ''  !! ' ' x $COLUMN_SEPARATION;
    }
    put '';

    # Data
    for $BODY .. $LEG -> $part {
        print left_justify(($part + 1).Str, $COLUMN_WIDTH + $COLUMN_SEPARATION, ' ');
        print left_justify(@PART_NAME[$part].tc, $COLUMN_WIDTH + $COLUMN_SEPARATION, ' ');
        put @PART_QUANTITY[$part];
    }

}

# Clear the screen, print the instructions and wait for a keypress.
#
sub print_instructions {

    clear_screen;
    put 'Bug';
    put $INSTRUCTIONS;
    print_parts_table;
    prompt "\nPress Enter to start. ";

}

# Print a bug head.
#
sub print_head {

    put '        HHHHHHH';
    put '        H     H';
    put '        H O O H';
    put '        H     H';
    put '        H  V  H';
    put '        HHHHHHH';

}

# Print the given bug.
#
sub print_bug(Bug $bug) {

    if $bug.feelers > 0 {
        for 0 ..^ $FEELER_LENGTH {
            print '        ';
            for 0 ..^ $bug.feelers {
                print ' ', $bug.feeler_type;
            }
            put '';
        }
    }
    if $bug.head {
        print_head;
    }
    if $bug.neck {
        for 0 ..^ $NECK_LENGTH {
            put '          N N';
        }
    }
    if $bug.body {
        put '     BBBBBBBBBBBB';
        for 0 ..^ $BODY_HEIGHT -> $i {
            put '     B          B';
        }
        if $bug.tail {
            put 'TTTTTB          B';
        }
        put '     BBBBBBBBBBBB';
    }
    if $bug.legs > 0 {
        for 0 ..^ $LEG_LENGTH -> $i {
            print '    ';
            for 0 ..^ $bug.legs -> $j {
                print ' L';
            }
            put '';
        }
    }

}

# Return `True` if the given bug is finished; otherwise return `False`.
#
sub finished(Bug $bug --> Bool) {

    return ($bug.feelers == $MAX_FEELERS and $bug.tail and $bug.legs == $MAX_LEGS);

}

# Array to convert a number to its equilavent text.
#
constant @AS_TEXT = (
    'no',
    'a',
    'two',
    'three',
    'four',
    'five',
    'six' ); # $MAX_LEGS

# Return a string containing the given number and noun in their proper form.
#
sub plural(Int $number, Str $noun --> Str) {

    return "@AS_TEXT[$number] $noun {($number > 1)  ?? 's'  !! ''}";

}

# Add the given part to the given player's bug.
#
sub add_part(Int $part, Player $player --> Bool) {

    my Bool $changed = False;
    given ($part) {
        when $BODY {
            if $player.bug.body {
                put ", but {$player.pronoun} already have a body.";
            } else {
                put "; {$player.pronoun} now have a body:";
                $player.bug.body = True;
                $changed = True;
            }
        }
        when $NECK {
            if $player.bug.neck {
                put ", but {$player.pronoun} already have a neck.";
            } elsif not $player.bug.body {
                put ", but {$player.pronoun} need a body first.";
            } else {
                put "; {$player.pronoun} now have a neck:";
                $player.bug.neck = True;
                $changed = True;
            }
        }
        when $HEAD {
            if $player.bug.head {
                put ", but {$player.pronoun} already have a head.";
            } elsif not $player.bug.neck {
                put ", but {$player.pronoun} need a a neck first.";
            } else {
                put "; {$player.pronoun} now have a head:";
                $player.bug.head = True;
                $changed = True;
            }
        }
        when $FEELER {
            if $player.bug.feelers == $MAX_FEELERS {
                put ", but {$player.pronoun} have two feelers already.";
            } elsif not $player.bug.head {
                put ", but  {$player.pronoun} need a head first.";
            } else {
                $player.bug.feelers += 1;
                put "; {$player.pronoun} now have {plural($player.bug.feelers, 'feeler')}:";
                $changed = True;
            }
        }
        when $TAIL {
            if $player.bug.tail {
                put ", but {$player.pronoun} already have a tail.";
            } elsif not $player.bug.body {
                put ", but {$player.pronoun} need a body first.";
            } else {
                put "; {$player.pronoun} now have a tail:";
                $player.bug.tail = True;
                $changed = True;
            }
        }
        when $LEG {
            if $player.bug.legs == $MAX_LEGS {
                put ", but {$player.pronoun} have {@AS_TEXT[$MAX_LEGS]} feet already.";
            } elsif not $player.bug.body {
                put ", but, {$player.pronoun} need a body first.";
            } else {
                $player.bug.legs += 1;
                put "; {$player.pronoun} now have {plural($player.bug.legs, 'leg')}:";
                $changed = True;
            }
        }
    }
    return $changed;

}

# Ask the user to press the Enter key, wait for the input, then erase the
# prompt text.
#
sub prompt_dice {

    prompt('Press Enter to roll the dice. ');
    erase_previous_line;

}

# Play one turn for the given player, rolling the dice and updating his bug.
#
sub turn(Player $player) {

    prompt_dice;
    my Int $part = (0 .. 5).pick;
    print "{$player.pronoun.tc} rolled a {$part + 1} ({@PART_NAME[$part]})";
    if add_part($part, $player) {
        put '';
        print_bug($player.bug);
    }
    put '';

}

# Print a message about the winner.
#
sub print_winner {

    if finished($human.bug) and finished($computer.bug) {
        put 'Both of our bugs are finished in the same number of turns!';
    } elsif finished($human.bug) {
        put "{$human.possessive} bug is finished.";
    } elsif finished($computer.bug) {
        put "{$computer.possessive} bug is finished.";
    }

}

# Return `True` if either bug is finished, i.e. the game ending condition.
#
sub game_over(--> Bool) {

    return finished($human.bug) or finished($computer.bug);

}

# Execute the game loop.
#
sub play {

    clear_screen;
    while not game_over() {
        turn($human);
        turn($computer);
    };
    print_winner;

}

# Init the players' data before a new game.
#
sub init {

    $human.pronoun = 'you';
    $human.possessive = 'Your';
    $human.bug.feeler_type = 'A';
    $computer.pronoun = 'I';
    $computer.possessive = 'My';
    $computer.bug.feeler_type = 'F';

}

init;
print_credits;
print_instructions;
play;
put 'I hope you enjoyed the game, play it again soon!!';

En Swift

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in Swift:
    Copyright (c) 2023, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written in 2023-11-04/05.

Last modified: 20231105T0109+0100.
*/


struct Bug {
    var body: Bool = false
    var neck: Bool = false
    var head: Bool = false
    var feelers: Int = 0
    var feelerType: Character = " "
    var tail: Bool = false
    var legs: Int = 0
}

struct Player {
    var pronoun: String = ""
    var possessive: String = ""
    var bug: Bug = Bug()
}

// Players.
var computer = Player(pronoun: "I", possessive: "My")
computer.bug.feelerType = "F"
var human = Player(pronoun: "you", possessive: "Your")
human.bug.feelerType = "A"

enum Part: Int, CaseIterable {
    case Body = 1, Neck, Head, Feeler, Tail, Leg
    func quantity() -> Int {
        switch self {
            case .Body: return 1
            case .Neck: return 1
            case .Head: return 1
            case .Feeler: return 2
            case .Tail: return 1
            case .Leg: return 6
        }
    }
}

// Bug body attributes.
let BODY_HEIGHT = 2
let FEELER_LENGTH = 4
let LEG_LENGTH = 2
let MAX_FEELERS = 2
let MAX_LEGS = 6
let NECK_LENGTH = 2

// Move the cursor to the home position.
func home() {
    print("\u{001B}[H", terminator: "")
}

// Clear the screen and move the cursor to the home position.
func clear() {
    print("\u{001B}[2J", terminator: "")
    home()
}

// Move the cursor up by the given number of rows (defaults to 1), without
// changing the column position.
func cursorUp(rows: UInt8 = 1) {
    print("\u{001B}[\(rows)A", terminator: "")
}

// Erase the current line, without moving the cursor position.
func eraseLine() {
    print("\u{001B}[2K", terminator: "")
}

// Move the cursor to the previous row, without changing the column position,
// and erase its line.
func erasePreviousLine() {
    cursorUp()
    eraseLine()
}

// Clear the screen, display the credits and wait for a keypress.
func printCredits() {
    clear()
    print("Bug\n")
    print("Original version in BASIC:")
    print("    Brian Leibowitz, 1978.")
    print("    Creative computing (Morristown, New Jersey, USA), 1978.\n")
    print("This version in Swift:")
    print("    Copyright (c) 2023, Marcos Cruz (programandala.net)")
    print("    SPDX-License-Identifier: Fair\n")
    print("Press Enter to read the instructions. ", terminator: "")
    let _ = readLine()
}

let instructions = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.

I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.

If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
"""

extension String {
    func capitalized() -> String {
        return self.prefix(1).uppercased() + self.dropFirst()
    }
}

func leftJustify(text: String, width: Int, pad: String) -> String {
    return String((text + String(repeating: pad, count: width)).prefix(width))
}

// Print a table with the bug parts' description.
func printPartsTable() {

    let COLUMNS = 3
    let COLUMN_WIDTH = 8
    let RULER = String(repeating: "-", count: COLUMN_WIDTH)
    let COLUMN_SEPARATION = 2
    let SEPARATOR = String(repeating: " ", count: COLUMN_SEPARATION)

    let headers = ["Number", "Part", "Quantity"]
    for header in headers {
        print(
            leftJustify(text: header, width: COLUMN_WIDTH, pad: " "),
            terminator: SEPARATOR
        )
    }
    print()

    // Rulers
    for _ in 0 ..< COLUMNS {
        print(RULER, terminator: SEPARATOR)
    }
    print()

    // Data
    for part in Part.allCases {
        print(
            leftJustify(text: String(part.rawValue), width: COLUMN_WIDTH, pad: " "),
            leftJustify(text: String(describing: part), width: COLUMN_WIDTH, pad: " "),
            part.quantity(),
            separator: SEPARATOR,
            terminator: ""
        )
        print()
    }

}

// Clear the screen, print the instructions and wait for a keypress.
func printInstructions() {
    clear()
    print("Bug\n")
    print(instructions)
    print()
    printPartsTable()
    print("\nPress Enter to start. ", terminator: "")
    let _ = readLine()
}

// Print a bug head.
func printHead() {
    print("        HHHHHHH")
    print("        H     H")
    print("        H O O H")
    print("        H     H")
    print("        H  V  H")
    print("        HHHHHHH")
}

// Print the given bug.
func printBug(bug: Bug) {
    if bug.feelers > 0 {
        for _ in 0 ..< FEELER_LENGTH {
            print("        ", terminator: "")
            for _ in 0 ..< bug.feelers {
                print(" ", bug.feelerType, terminator: "")
            }
            print()
        }
    }
    if bug.head {
        printHead()
    }
    if bug.neck {
        for _ in 0 ..< NECK_LENGTH {
            print("          N N")
        }
    }
    if bug.body {
        print("     BBBBBBBBBBBB")
        for _ in 0 ..< BODY_HEIGHT {
            print("     B          B")
        }
        if bug.tail {
            print("TTTTTB          B")
        }
        print("     BBBBBBBBBBBB")
    }
    if bug.legs > 0 {
        for _ in 0 ..< LEG_LENGTH {
            print("    ", terminator: "")
            for _ in 0 ..< bug.legs {
                print(" L", terminator: "")
            }
            print()
        }
    }
}

// Return `true` if the given bug is finished; otherwise return `false`.
func finished(bug: Bug) -> Bool {
    return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS
}

// Array to convert a number to its equilavent text.
let asText = [
    "no",
    "a",
    "two",
    "three",
    "four",
    "five",
    "six" ] // MAX_LEGS

// Return a string containing the given number and noun in their proper form.
func plural(number: Int, noun: String) -> String {
    return "\(asText[number]) \(noun)\((number > 1) ? "s" : "")"
}

// Add the given part to the given player's bug.
func addPart(part: Part, player: inout Player) -> Bool {
    var changed = false
    switch part {
    case Part.Body:
        if player.bug.body {
            print(", but", player.pronoun, "already have a body.")
        } else {
            print(";", player.pronoun, "now have a body:")
            player.bug.body = true
            changed = true
        }
    case Part.Neck:
        if player.bug.neck {
            print(", but", player.pronoun, "you already have a neck.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            print(";", player.pronoun, "now have a neck:")
            player.bug.neck = true
            changed = true
        }
    case Part.Head:
        if player.bug.head {
            print(", but", player.pronoun, "already have a head.")
        } else if !player.bug.neck {
            print(", but", player.pronoun, "need a a neck first.")
        } else {
            print(";", player.pronoun, "now have a head:")
            player.bug.head = true
            changed = true
        }
    case Part.Feeler:
        if player.bug.feelers == MAX_FEELERS {
            print(", but", player.pronoun, "have two feelers already.")
        } else if !player.bug.head {
            print(", but", player.pronoun, "need a head first.")
        } else {
            player.bug.feelers += 1
            print("; \(player.pronoun) now have \(plural(number: player.bug.feelers, noun: "feeler")):")
            changed = true
        }
    case Part.Tail:
        if player.bug.tail {
            print(", but", player.pronoun, "already have a tail.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            print(";", player.pronoun, "now have a tail:")
            player.bug.tail = true
            changed = true
        }
    case Part.Leg:
        if player.bug.legs == MAX_LEGS {
            print(", but", player.pronoun, "have",
                asText[MAX_LEGS], "feet already.")
        } else if !player.bug.body {
            print(", but", player.pronoun, "need a body first.")
        } else {
            player.bug.legs += 1
            print("; \(player.pronoun) now have \(plural(number: player.bug.legs, noun: "leg")):")
            changed = true
        }
    }
    return changed
}

// Ask the user to press the Enter key, wait for the input, then erase the
// prompt text.
func prompt() {
    print("Press Enter to roll the dice. ", terminator: "")
    let _ = readLine()
    erasePreviousLine()
}

// Play one turn for the given player, rolling the dice and updating his bug.
func turn(_ player: inout Player) {
    prompt()
    let number = Int.random(in: 1...6)
    let part = Part(rawValue: number)!
    print("\(player.pronoun.capitalized()) rolled a \(number) (\(String(describing: part).lowercased()))", terminator: "")
    if addPart(part: part, player: &player) {
        print()
        printBug(bug: player.bug)
    }
    print()
}

// Print a message about the winner.
func printWinner() {
    if finished(bug: human.bug) && finished(bug: computer.bug) {
        print("Both of our bugs are finished in the same number of turns!")
    } else if finished(bug: human.bug) {
        print(human.possessive, "bug is finished.")
    } else if finished(bug: computer.bug) {
        print(computer.possessive, "bug is finished.")
    }
}

// Return `true` if either bug is finished, i.e. the game ending condition.
func gameOver() -> Bool {
    return finished(bug: human.bug) || finished(bug: computer.bug)
}

// Execute the game loop.
func play() {
    clear()
    while !gameOver() {
        turn(&human)
        turn(&computer)
    }
    printWinner()
}

printCredits()
printInstructions()
play()
print("I hope you enjoyed the game, play it again soon!!")

En V

/*
Bug

Original version in BASIC:
    Brian Leibowitz, 1978.
    Creative Computing (Morristown, New Jersey, USA), 1978.

This version in V:
    Copyright (c) 2025, Marcos Cruz (programandala.net)
    SPDX-License-Identifier: Fair

Written on 2025-01-04.

Last modified: 20250504T0045+0200.
*/
import os
import rand
import strings
import term

struct Bug {
mut:
    body        bool
    neck        bool
    head        bool
    feelers     int
    feeler_type rune
    tail        bool
    legs        int
}

struct Player {
mut:
    pronoun    string
    possessive string
    bug        Bug
}

enum Part {
    body
    neck
    head
    feeler
    tail
    leg
}

// Bug body attributes.
//
const body_height = 2
const feeler_length = 4
const leg_length = 2
const max_feelers = 2
const max_legs = 6
const neck_length = 2

// Clear the screen, display the credits and wait for a keypress.
//
fn print_credits() {
    term.clear()
    println('Bug\n')
    println('Original version in BASIC:')
    println('    Brian Leibowitz, 1978.')
    println('    Creative computing (Morristown, New Jersey, USA), 1978.\n')
    println('This version in V:')
    println('    Copyright (c) 2025, Marcos Cruz (programandala.net)')
    println('    SPDX-License-Identifier: Fair\n')
    os.input('Press Enter to read the instructions. ')
}

const columns = 3
const column_width = 8
const column_separation = 2

// Return the given string padded with spaces at the right up to
// the given total length.
//
fn left_justified(s string, l int) string {
    return s + strings.repeat(` `, l - s.len)
}

// Print a table with the bug parts' description.
//
fn print_parts_table() {
    // Headers
    header := ['Number', 'Part', 'Quantity']
    for i in 0 .. columns {
        print(left_justified(header[i], column_width + column_separation))
    }
    println('')

    // Rulers
    ruler := strings.repeat(`-`, column_width)
    for i in 0 .. columns {
        print(ruler)
        if i != columns - 1 {
            print(strings.repeat(` `, column_separation))
        }
    }
    println('')

    // Data
    part_quantity := [1, 1, 1, 2, 1, 6]
    mut i := 1
    $for part in Part.values {
        print(left_justified('${i}', column_width + column_separation))
        print(left_justified('${part.name.capitalize()}', column_width + column_separation))
        println(part_quantity[part.value])
        i++
    }
}

// Clear the screen, print the instructions and wait for a keypress.
//
fn print_instructions() {
    term.clear()
    println('Bug')
    println('\nThe object is to finish your bug before I finish mine. Each number')
    println('stands for a part of the bug body.')
    println('\nI will roll the die for you, tell you what I rolled for you, what the')
    println('number stands for, and if you can get the part. If you can get the')
    println('part I will give it to you. The same will happen on my turn.')
    println('\nIf there is a change in either bug I will give you the option of')
    println('seeing the pictures of the bugs. The numbers stand for parts as')
    println('follows:\n')
    print_parts_table()
    os.input('\nPress Enter to start. ')
}

// Print a bug head.
//
fn print_head() {
    println('        HHHHHHH')
    println('        H     H')
    println('        H O O H')
    println('        H     H')
    println('        H  V  H')
    println('        HHHHHHH')
}

// Print the given bug.
//
fn print_bug(bug Bug) {
    if bug.feelers > 0 {
        for _ in 0 .. feeler_length {
            print('        ')
            for _ in 0 .. bug.feelers {
                print(' ${bug.feeler_type}')
            }
            println('')
        }
    }
    if bug.head {
        print_head()
    }
    if bug.neck {
        for _ in 0 .. neck_length {
            println('          N N')
        }
    }
    if bug.body {
        println('     BBBBBBBBBBBB')
        for _ in 0 .. body_height {
            println('     B          B')
        }
        if bug.tail {
            println('TTTTTB          B')
        }
        println('     BBBBBBBBBBBB')
    }
    if bug.legs > 0 {
        for _ in 0 .. leg_length {
            print('    ')
            for _ in 0 .. bug.legs {
                print(' L')
            }
            println('')
        }
    }
}

// Return `true` if the given bug is finished; otherwise return `false`.
//
fn finished(bug Bug) bool {
    return bug.feelers == max_feelers && bug.tail && bug.legs == max_legs
}

// Return a random number from 1 to 6 (inclusive).
//
fn dice() int {
    return rand.intn(6) or { 0 } + 1
}

// Array to convert a number to its equilavent text.
//
const as_text = ['no', 'a', 'two', 'three', 'four', 'five', 'six']

// Return a string containing the given number and noun in their proper form.
//
fn plural(number int, noun string) string {
    return as_text[number] + ' ' + noun + (if number > 1 {
        's'
    } else {
        ''
    })
}

// Add the given part to the given player's bug.
//
fn add_part(part Part, mut player Player) bool {
    mut changed := false
    match part {
        .body {
            if player.bug.body {
                println(', but ${player.pronoun} already have a body.')
            } else {
                println('; ${player.pronoun} now have a body:')
                player.bug.body = true
                changed = true
            }
        }
        .neck {
            if player.bug.neck {
                println(', but ${player.pronoun} already have a neck.')
            } else if !player.bug.body {
                println(', but ${player.pronoun} need a body first.')
            } else {
                println('; ${player.pronoun} now have a neck:')
                player.bug.neck = true
                changed = true
            }
        }
        .head {
            if player.bug.head {
                println(', but ${player.pronoun} already have a head.')
            } else if !player.bug.neck {
                println(', but ${player.pronoun} need a a neck first.')
            } else {
                println('; ${player.pronoun} now have a head:')
                player.bug.head = true
                changed = true
            }
        }
        .feeler {
            if player.bug.feelers == max_feelers {
                println(', but ${player.pronoun} have two feelers already.')
            } else if !player.bug.head {
                println(', but ${player.pronoun} need a head first.')
            } else {
                player.bug.feelers += 1
                print('; ${player.pronoun} now have ${plural(player.bug.feelers, 'feeler')}')
                println(':')
                changed = true
            }
        }
        .tail {
            if player.bug.tail {
                println(', but ${player.pronoun} already have a tail.')
            } else if !player.bug.body {
                println(', but ${player.pronoun} need a body first.')
            } else {
                println('; ${player.pronoun} now have a tail:')
                player.bug.tail = true
                changed = true
            }
        }
        .leg {
            if player.bug.legs == max_legs {
                println(', but ${player.pronoun} have ${as_text[max_legs]} feet already.')
            } else if !player.bug.body {
                println(', but ${player.pronoun} need a body first.')
            } else {
                player.bug.legs += 1
                print('; ${player.pronoun} now have ${plural(player.bug.legs, 'leg')}')
                println(':')
                changed = true
            }
        }
    }
    return changed
}

// Ask the user to press the Enter key, wait for the input, then erase the
// prompt text.
//
fn prompt() {
    os.input('Press Enter to roll the dice. ')
    term.clear_previous_line()
}

// Play one turn for the given player, rolling the dice and updating his bug.
//
fn turn(mut player Player) {
    prompt()
    number := dice()
    unsafe {
        part := Part(number - 1)
        print('${player.pronoun.capitalize()} rolled a ${number} (${part})')
        if add_part(part, mut player) {
            println('')
            print_bug(player.bug)
        }
    }
    println('')
}

// Print a message about the winner.
//
fn print_winner(human Player, computer Player) {
    if finished(human.bug) && finished(computer.bug) {
        println('Both of our bugs are finished in the same number of turns!')
    } else if finished(human.bug) {
        println('${human.possessive} bug is finished.')
    } else if finished(computer.bug) {
        println('${computer.possessive} bug is finished.')
    }
}

// Return `true` if either bug is finished, i.e. the game ending condition.
//
fn game_over(human Player, computer Player) bool {
    return finished(human.bug) || finished(computer.bug)
}

// Execute the game loop.
//
fn play() {
    term.clear()

    mut computer := Player{
        pronoun:    'you'
        possessive: 'Your'
        bug:        Bug{
            feeler_type: `A`
        }
    }

    mut human := Player{
        pronoun:    'I'
        possessive: 'My'
        bug:        Bug{
            feeler_type: `F`
        }
    }

    for !game_over(human, computer) {
        turn(mut human)
        turn(mut computer)
    }
    print_winner(human, computer)
}

fn main() {
    print_credits()
    print_instructions()
    play()
    println('I hope you enjoyed the game, play it again soon!!')
}

Páginas relacionadas

Basics off
Metaproyecto sobre los proyectos «Basics of…».

Enlaces externos relacionados