Basics of Pike

Description of the page content

Conversion of old BASIC programs to Pike in order to learn the basics of this language.

Tags:

3D Plot

#! /usr/bin/env pike

// 3D Plot

// Original version in BASIC:
//  Creative Computing (Morristown, New Jersey, USA), ca. 1980.

// This version in Pike:
//  Copyright (c) 2024, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2024-12-17.
//
// Last modified: 20250311T0155+0100.

constant SPACE = " ";
constant DOT = "*";
constant WIDTH = 56;

void clear_screen() {
    write("\x1B[0;0H\x1B[2J");
}

void prompt(string s) {
    write(s);
    Stdio.stdin->gets();
}

void print_credits() {
    write("3D Plot\n\n");
    write("Original version in BASIC:\n");
    write("    Creative computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2024, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n\n");
    prompt("Press Enter to start. ");
}

float a(float z) {
    return 30.0 * exp(-z * z / 100.0);
}

void draw() {

    int l = 0;
    int z = 0;
    int y1 = 0;

    array(string) line = allocate(WIDTH);

    for (float x = -30.0; x <= 30.0; x += 1.5) {

        for (int i = 0; i < WIDTH; i += 1)
            line[i] = SPACE;

        l = 0;
        y1 = 5 * (int) (sqrt(900.0 - x * x) / 5.0);

        for (int y = y1; y >= -y1; y += -5) {
            z = (int) (25.0 + a(sqrt(x * x + (float) (y * y))) - 0.7 * (float) y);
            if (z > l) {
                l = z;
                line[z] = DOT;
            }
        } // y loop

        for (int pos = 0; pos < WIDTH; pos += 1)
            write(line[pos]);

        write("\n");

    } // x loop

}

void main() {
    clear_screen();
    print_credits();
    clear_screen();
    draw();
}

Bagels

#!/usr/bin/env pike

// Bagels

// Original version in BASIC:
//  D. Resek, P. Rowe, 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: 20250324T1737+0100.

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

constant NORMAL_STYLE = 0;

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

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();
}

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

// Clear the screen, display the credits and wait for a keypress.
//
void print_credits() {
    clear_screen();
    write("Bagels\n");
    write("Number guessing game\n\n");
    write("Original source unknown but suspected to be:\n");
    write("    Lawrence Hall of Science, U.C. Berkely.\n\n");
    write("Original version in BASIC:\n");
    write("    D. Resek, P. Rowe, 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. ");
}

// Clear the screen, print the instructions and wait for a keypress.
//
void print_instructions() {
    clear_screen();
    write("Bagels\n");
    write("Number guessing game\n\n");
    write("I am thinking of a three-digit number that has no two digits the same.\n");
    write("Try to guess it and I will give you clues as follows:\n\n");
    write("   PICO   - one digit correct but in the wrong position\n");
    write("   FERMI  - one digit correct and in the right position\n");
    write("   BAGELS - no digits correct\n\n");
    accept_string("Press Enter to start. ");
}

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

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

// Return `true` if the given string is "yes" or a synonym.
//
bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

// Return `true` if the given string is "no" or a synonym.
//
bool is_no(string s) {
    switch(lower_case(s)) {
        case "n":
        case "no":
        case "nope":
            return true;
        default:
            return false;
    }
}

// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
bool yes(string prompt) {
    while (true) {
        string answer = accept_string(prompt);
        if (is_yes(answer)) {
            return true;
        }
        if (is_no(answer)) {
            return false;
        }
    }
}

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

constant DIGITS = 3;

bool is_digit(int ascii_code) {
    return (ascii_code >= '0') && (ascii_code <= '9');
}

// Print the given prompt and return an array with a three-digit number type by
// the user.
//
array(int) get_input(string prompt) {
    array(int) user_digit = allocate(DIGITS);
    constant ASCII_0 = '0';
    get_loop: while (true) {
        string input = accept_string(prompt);
        if (sizeof(input) != DIGITS) {
            write("Remember it's a %d-digit number.\n", DIGITS);
            continue get_loop;
        }
        for (int pos = 0; pos < sizeof(input); pos += 1) {
            int digit = input[pos];
            if (is_digit(digit)) {
                user_digit[pos] = digit - ASCII_0;
            } else {
                write("What?\n");
                continue get_loop;
            }
        }
        if (is_any_repeated(user_digit)) {
            write("Remember my number has no two digits the same.\n");
            continue get_loop;
        }
        break;
    }
    return user_digit;
}

// Return three random digits.
//
array(int) random_number() {
    array(int) random_digit = allocate(DIGITS);
    for (int i = 0; i < DIGITS; i += 1) {
        digit_loop: while (true) {
            random_digit[i] = random(10);
            for (int j = 0; j < i; j += 1) {
                if (i != j && random_digit[i] == random_digit[j]) {
                    continue digit_loop;
                }
            }
            break;
        }
    }
    return random_digit;
}

// Return `true` if any of the given numbers is repeated; otherwise return
// `false`.
//
bool is_any_repeated(array(int) numbers) {
    array(int) filtered_numbers = Array.uniq(numbers);
    return !equal(filtered_numbers, numbers);
}

// Init and run the game loop.
//
void play() {
    constant TRIES = 20;
    int score = 0;
    int fermi = 0; // counter
    int pico = 0; // counter
    array(int) computer_number = allocate(DIGITS);
    array(int) user_number = allocate(DIGITS);
    while (true) {
        clear_screen();
        array(int) computer_number = random_number();
        write("O.K.  I have a number in mind.\n");
        for (int guess = 1; guess <= TRIES; guess += 1) {
            // XXX TMP
            // write("My number: ");
            // for (int i = 0; i < DIGITS; i += 1) {
            //  write("%d", computer_number[i]);
            // }
            // write("\n");
            user_number = get_input(sprintf("Guess #%02d: ", guess));
            fermi = 0;
            pico = 0;
            for (int i = 0; i < DIGITS; i += 1) {
                for (int j = 0; j < DIGITS; j += 1) {
                    if (user_number[i] == computer_number[j]) {
                        if (i == j) {
                            fermi += 1;
                        } else {
                            pico += 1;
                        }
                    }
                }
            }
            if (pico + fermi == 0) {
                write("BAGELS\n");
            } else {
                write("%s%s\n", "PICO " * pico, "FERMI " * fermi);
                if (fermi == DIGITS) {
                    break;
                }
            }
        }
        if (fermi == DIGITS) {
            write("You got it!!!\n");
            score += 1;
        } else {
            write("Oh well.\n");
            write("That's %d guesses.  My number was ", TRIES);
            for (int i = 0; i < DIGITS; i += 1) {
                write("%d", computer_number[i]);
            }
            write(".\n");
        }
        if (!yes("Play again? ")) {
            break;
        }
    }
    if (score != 0) {
        write("A %d-point bagels, buff!!\n", score);
    }
    write("Hope you had fun.  Bye.\n");
}

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

Bug

#!/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();
}

Bunny

#! /usr/bin/env pike

// Bunny

// Original version in BASIC:
//  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-08.
//
// Last modified: 20250310T0301+0100.

void clear_screen() {
    write("\x1B[0;0H\x1B[2J");
}

void press_enter(string prompt) {
    write(prompt);
    Stdio.stdin->gets();
}

void print_credits() {
    write("Bunny\n\n");
    write("Original version in BASIC:\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");
    press_enter("Press Enter to start the program. ");
}

constant WIDTH = 53; // size of the line buffer

array(string) line = ({}); // line buffer

// Init the line buffer.
//
void init_line() {
    for (int column = 0; column < WIDTH; column += 1) {
        line += ({" "});
    }
}

// Clear the line buffer with spaces.
//
void clear_line() {
    for (int column = 0; column < WIDTH; column += 1) {
        line[column] = " ";
    }
}

array(string) letter = ({"B", "U", "N", "N", "Y"});

int LETTERS = sizeof(letter);

constant EOL = -1; // end of line identifier

array(int) data = ({
    1, 2, EOL, 0, 2, 45, 50, EOL, 0, 5, 43, 52, EOL, 0, 7, 41, 52, EOL,
    1, 9, 37, 50, EOL, 2, 11, 36, 50, EOL, 3, 13, 34, 49, EOL, 4, 14,
    32, 48, EOL, 5, 15, 31, 47, EOL, 6, 16, 30, 45, EOL, 7, 17, 29, 44,
    EOL, 8, 19, 28, 43, EOL, 9, 20, 27, 41, EOL, 10, 21, 26, 40, EOL,
    11, 22, 25, 38, EOL, 12, 22, 24, 36, EOL, 13, 34, EOL, 14, 33, EOL,
    15, 31, EOL, 17, 29, EOL, 18, 27, EOL, 19, 26, EOL, 16, 28, EOL,
    13, 30, EOL, 11, 31, EOL, 10, 32, EOL, 8, 33, EOL, 7, 34, EOL, 6,
    13, 16, 34, EOL, 5, 12, 16, 35, EOL, 4, 12, 16, 35, EOL, 3, 12, 15,
    35, EOL, 2, 35, EOL, 1, 35, EOL, 2, 34, EOL, 3, 34, EOL, 4, 33,
    EOL, 6, 33, EOL, 10, 32, 34, 34, EOL, 14, 17, 19, 25, 28, 31, 35,
    35, EOL, 15, 19, 23, 30, 36, 36, EOL, 14, 18, 21, 21, 24, 30, 37, 37,
    EOL, 13, 18, 23, 29, 33, 38, EOL, 12, 29, 31, 33, EOL, 11, 13, 17,
    17, 19, 19, 22, 22, 24, 31, EOL, 10, 11, 17, 18, 22, 22, 24, 24, 29,
    29, EOL, 22, 23, 26, 29, EOL, 27, 29, EOL, 28, 29, EOL });

int data_index = 0;

int datum() {
    return data[data_index++];
}

void draw() {
    init_line();
    while (data_index < sizeof(data)) {
        int first_column = datum();
        if (first_column == EOL) {
            foreach(line, string c) {
                write("%s", c);
            }
            write("\n");
            clear_line();
        } else {
            int last_column = datum();
            for (int column = first_column; column <= last_column; column += 1) {
                line[column] = letter[column % LETTERS];
            }
        }
    }
}

void main() {
    clear_screen();
    print_credits();
    clear_screen();
    draw();
}

Chase

#!/usr/bin/env pike

// Chase

// Original version in BASIC:
//  Anonymous.
//  Published in 1977 in "The Best of Creative Computing", Volume 2.
//  https://www.atariarchives.org/bcc2/showpage.php?page=253

// This version in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
//  Written on 2025-03-11.
//  Last modified: 20250731T1954+0200.

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

void set_cursor_position(int line, int col) {
    write("\x1B[%d;%dH", line, col);
}

void hide_cursor() {
    write("\x1B[?25l");
}

void show_cursor() {
    write("\x1B[?25h");
}

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

void reset_attributes() {
    set_style(NORMAL_STYLE);
}

void erase_line_to_end() {
    write("\x1B[K");
}

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

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

// Config {{{1
// =============================================================

constant DEFAULT_INK = WHITE + FOREGROUND;
constant INPUT_INK = BRIGHT + GREEN + FOREGROUND;
constant INSTRUCTIONS_INK = YELLOW + FOREGROUND;
constant TITLE_INK = BRIGHT + RED + FOREGROUND;

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

// Arena

constant ARENA_WIDTH = 20;
constant ARENA_HEIGHT = 10;
constant ARENA_LAST_X = ARENA_WIDTH - 1;
constant ARENA_LAST_Y = ARENA_HEIGHT - 1;
constant ARENA_ROW = 3;

array(array(string)) arena = allocate(ARENA_HEIGHT, allocate(ARENA_WIDTH, ""));

constant EMPTY = " ";
constant FENCE = "X";
constant MACHINE = "m";
constant HUMAN = "@";

constant FENCES = 15; // inner obstacles, not the border

// The end

enum End {
    NOT_YET,
    QUIT,
    ELECTRIFIED,
    KILLED,
    VICTORY
}

End the_end = NOT_YET;

// Machines

constant MACHINES = 5;
constant MACHINES_DRAG = 2; // probability not moving: 0=0%, 1=50%, 2=66%, 3=75%, etc.

array(int) machine_x = allocate(MACHINES);
array(int) machine_y = allocate(MACHINES);
array(bool) operative = allocate(MACHINES);

int destroyed_machines = 0; // counter

// Human

int human_x = 0;
int human_y = 0;

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

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

string get_string(string prompt) {
    set_style(INPUT_INK);
    string s = accept_string(prompt);
    set_style(DEFAULT_INK);
    return s;
}

void press_enter(string prompt) {
    accept_string(prompt);
}

bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

bool is_no(string s) {
    switch(lower_case(s)) {
        case "n":
        case "no":
        case "nope":
            return true;
        default:
            return false;
    }
}

// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
bool yes(string prompt) {
    while (true) {
        string answer = get_string(prompt);
        if (is_yes(answer)) {
            return true;
        }
        if (is_no(answer)) {
            return false;
        }
    }
}

// Title, credits and instructions {{{1
// =============================================================

constant TITLE = "Chase";

void print_title() {
    set_style(TITLE_INK);
    write("%s\n", TITLE);
    set_style(DEFAULT_INK);
}

void print_credits() {
    print_title();
    write("\nOriginal version in BASIC:\n");
    write("    Anonymous.\n");
    write("    Published in \"The Best of Creative Computing\", Volume 2, 1977.\n");
    write("    https://www.atariarchives.org/bcc2/showpage.php?page=253\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n");
}

void print_instructions() {
    print_title();
    set_style(INSTRUCTIONS_INK);
    write("\nYou (%s) are in a high voltage maze with %d\n", HUMAN, MACHINES);
    write("security machines (%s) trying to kill you.\n", MACHINE);
    write("You must maneuver them into the maze (%s) to survive.\n\n", FENCE);
    write("Good luck!\n\n");
    write("The movement commands are the following:\n\n");
    write("    ↖  ↑  ↗\n");
    write("    NW N NE\n");
    write("  ←  W   E  →\n");
    write("    SW S SE\n");
    write("    ↙  ↓  ↘\n");
    write("\nPlus 'Q' to end the game.\n");
    set_style(DEFAULT_INK);
}

// Arena {{{1
// =============================================================

void print_arena() {
    set_cursor_position(ARENA_ROW, 1);
    for (int y = 0; y <= ARENA_LAST_Y; y += 1) {
        for (int x = 0; x <= ARENA_LAST_X; x += 1) {
            write(arena[y][x]);
        }
        write("\n");
    }
}

// If the given arena coordinates `y` and `x` are part of the border arena
// (i.e. its surrounding fence), return `true`, otherwise return `false`.
//
bool is_border(int y, int x) {
    return (y == 0) || (x == 0) || (y == ARENA_LAST_Y) || (x == ARENA_LAST_X);
}

int random_int_in_inclusive_range(int min, int max) {
    return random(max - min) + min;
}

// Place the given string at a random empty position of the arena and return
// the coordinates.
//
array(int) place(string s) {
    int y = 0;
    int x = 0;
    while (true) {
        y = random_int_in_inclusive_range(1, ARENA_LAST_Y - 1);
        x = random_int_in_inclusive_range(1, ARENA_LAST_X - 1);
        if (arena[y][x] == EMPTY) {
            break;
        }
    }
    arena[y][x] = s;
    return ({y, x});
}

// Inhabit the arena with the machines, the inner fences and the human.
//
void inhabit_arena() {
    array(int) coords;
    for (int m = 0; m < MACHINES; m += 1) {
        coords = place(MACHINE);
        machine_y[m] = coords[0];
        machine_x[m] = coords[1];
        operative[m] = true;
    }
    for (int i = 0; i < FENCES; i += 1) {
        place(FENCE);
    }
    coords = place(HUMAN);
    human_y = coords[0];
    human_x = coords[1];
}

// Clean the arena, i.e. empty it and add a surrounding fence.
//
void clean_arena() {
    for (int y = 0; y <= ARENA_LAST_Y; y += 1) {
        for (int x = 0; x <= ARENA_LAST_X; x += 1) {
            arena[y][x] = is_border(y, x) ? FENCE : EMPTY;
        }
    }
}

// Game {{{1
// =============================================================

void init_game() {
    clean_arena();
    inhabit_arena();
    destroyed_machines = 0;
    the_end = NOT_YET;
}

void move_machine(int m) {

    int maybe = 0;

    arena[machine_y[m]][machine_x[m]] = EMPTY;

    maybe = random(2);
    if (machine_y[m] > human_y) {
        machine_y[m] -= maybe;
    } else if (machine_y[m] < human_y) {
        machine_y[m] += maybe;
    }

    maybe = random(2);
    if (machine_x[m] > human_x) {
        machine_x[m] -= maybe;
    } else if (machine_x[m] < human_x) {
        machine_x[m] += maybe;
    }

    if (arena[machine_y[m]][machine_x[m]] == EMPTY) {
        arena[machine_y[m]][machine_x[m]] = MACHINE;
    } else if (arena[machine_y[m]][machine_x[m]] == FENCE) {
        operative[m] = false;
        destroyed_machines += 1;
        if (destroyed_machines == MACHINES) {
            the_end = VICTORY;
        }
    } else if (arena[machine_y[m]][machine_x[m]] == HUMAN) {
        the_end = KILLED;
    }

}

void maybe_move_machine(int m) {
    if (random(MACHINES_DRAG) == 0) {
        move_machine(m);
    }
}

void move_machines() {
    for (int m = 0; m < MACHINES; m += 1) {
        if (operative[m]) {
            maybe_move_machine(m);
        }
    }
}

// Read a user command; update `the_end` accordingly and return the direction
// increments.
//
array(int) get_move() {
    int y_inc = 0;
    int x_inc = 0;
    write("\n");
    erase_line_to_end();
    string command = lower_case(get_string("Command: "));
    switch (command) {
        case "q":
            the_end = QUIT;
            break;
        case "sw":
            y_inc = 1;
            x_inc = -1;
            break;
        case "s":
            y_inc = 1;
            break;
        case "se":
            y_inc = 1;
            x_inc = 1;
            break;
        case "w":
            x_inc = -1;
            break;
        case "e":
            x_inc = 1;
            break;
        case "nw":
            y_inc = -1;
            x_inc = -1;
            break;
        case "n":
            y_inc = -1;
            break;
        case "ne":
            y_inc = -1;
            x_inc = 1;

    }
    return ({y_inc, x_inc});
}

void play() {

    int y_inc = 0;
    int x_inc = 0;

    while (true) { // game loop

        clear_screen();
        print_title();
        init_game();

        while (true) { // action loop
            print_arena();
            array(int) coords = get_move();
            y_inc = coords[0];
            x_inc = coords[1];
            if (the_end == NOT_YET) {
                if (y_inc != 0 || x_inc != 0) {
                    arena[human_y][human_x] = EMPTY;
                    if (arena[human_y + y_inc][human_x + x_inc] == FENCE) {
                        the_end = ELECTRIFIED;
                    } else if (arena[human_y + y_inc][human_x + x_inc] == MACHINE) {
                        the_end = KILLED;
                    } else {
                        arena[human_y][human_x] = EMPTY;
                        human_y = human_y + y_inc;
                        human_x = human_x + x_inc;
                        arena[human_y][human_x] = HUMAN;
                        print_arena();
                        move_machines();
                    }
                }
            }
            if (the_end != NOT_YET) {
                break;
            }
        } // action loop;

        switch (the_end) {
            case QUIT:
                write("\nSorry to see you quit.\n");
                break;
            case ELECTRIFIED:
                write("\nZap! You touched the fence!\n");
                break;
            case KILLED:
                write("\nYou have been killed by a lucky machine.\n");
                break;
            case VICTORY:
                write("\nYou are lucky, you destroyed all machines.\n");

        }

        if (!yes("\nDo you want to play again? ")) {
            break;
        }
    } // game loop

    write("\nHope you don't feel fenced in.\n");
    write("Try again sometime.\n");
}

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

void main() {
    set_style(DEFAULT_INK);
    clear_screen();
    print_credits();
    press_enter("\nPress the Enter key to read the instructions. ");
    clear_screen();
    print_instructions();
    press_enter("\nPress the Enter key to start. ");
    play();
}

Diamond

#! /usr/bin/env pike

// Diamond

// Original version in BASIC:
//  Example included in Vintage BASIC 1.0.3.
//  http://www.vintage-basic.net

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

// Written on 2024-12-17.

// Last modified: 20250310T0308+0100

constant LINES = 17;

void main() {

    for (int i = 1; i <= LINES / 2 + 1; i += 1) {
        for (int j = 1; j <= (LINES + 1) / 2 - i + 1; j += 1)
            write(" ");
        for (int j = 1; j <= i * 2 - 1; j += 1)
            write("*");
        write("\n");
    }

    for (int i = 1; i <= LINES / 2; i += 1) {
        for (int j = 1; j <= i + 1; j += 1)
            write(" ");
        for (int j = 1; j <= ((LINES + 1) / 2 - i) * 2 - 1; j += 1)
            write("*");
        write("\n");
    }

}

Hammurabi

#!/usr/bin/env pike

// Hammurabi

// Description:
//  A simple text-based simulation game set in the ancient kingdom of Sumeria.

// Original program:
//  Written in FOCAL on a DEP PDP-8 by Rick Merrill, 1969.
//
// BASIC port:
//  Ported from FOCAL and modified for Edusystem 70 by David Ahl, c. 1973.
//  Modified for 8K Microsoft BASIC by Peter Turnbull, c. 1978.
//
// More details:
//  - https://en.wikipedia.org/wiki/Hamurabi_(video_game)
//  - https://www.mobygames.com/game/22232/hamurabi/

// This improved remake in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2025-03-11.
//
// Last modified: 20250731T1954+0200.
//
// Acknowledgment:
//  The following Python port was used as a reference of the original
//  variables: <https://github.com/jquast/hamurabi.py>.
//

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


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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

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();
}

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

constant ACRES_A_BUSHEL_CAN_SEED = 2; // yearly
constant ACRES_A_PERSON_CAN_SEED = 10; // yearly
constant ACRES_PER_PERSON = 10; // to calculate the initial acres of the city
constant BUSHELS_TO_FEED_A_PERSON = 20; // yearly
constant IRRITATION_LEVELS = 5; // after the switch in `show_irritation`
constant IRRITATION_STEP = MAX_IRRITATION / IRRITATION_LEVELS;
constant MAX_HARVESTED_BUSHELS_PER_ACRE = MIN_HARVESTED_BUSHELS_PER_ACRE + RANGE_OF_HARVESTED_BUSHELS_PER_ACRE - 1;
constant MIN_HARVESTED_BUSHELS_PER_ACRE = 17;
constant MAX_IRRITATION = 16;
constant PLAGUE_CHANCE = 0.15; // 15% yearly
constant RANGE_OF_HARVESTED_BUSHELS_PER_ACRE = 10;
constant YEARS = 10; // goverment period

constant DEFAULT_INK = FOREGROUND + WHITE;
constant INPUT_INK = FOREGROUND + BRIGHT + GREEN;
constant INSTRUCTIONS_INK = FOREGROUND + YELLOW;
constant RESULT_INK = FOREGROUND + BRIGHT + CYAN;
constant SPEECH_INK = FOREGROUND + BRIGHT + MAGENTA;
constant TITLE_INK = FOREGROUND + BRIGHT + WHITE;
constant WARNING_INK = FOREGROUND + BRIGHT + RED;

enum Result {
    VERY_GOOD,
    NOT_TOO_BAD,
    BAD,
    VERY_BAD
}

int acres = 0;
int bushels_eaten_by_rats = 0;
int bushels_harvested = 0;
int bushels_harvested_per_acre = 0;
int bushels_in_store = 0;
int bushels_to_feed_with = 0;
int dead = 0;
int infants = 0;
int irritation = 0; // counter (0 ..= 99)
int population = 0;
int starved_people_percentage = 0;
int total_dead = 0;

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

constant CREDITS =
"Hammurabi\n" +
"\n" +
"Original program:\n" +
"  Written in FOCAL on a DEP PDP-8 by Rick Merrill, 1969.\n" +
"\n" +
"BASIC port:\n" +
"  Ported from FOCAL and modified for Edusystem 70 by David Ahl, c. 1973.\n" +
"  Modified for 8K Microsoft BASIC by Peter Turnbull, c. 1978.\n" +
"\n" +
"This improved remake in Pike:\n" +
"  Copyright (c) 2025, Marcos Cruz (programandala.net)\n" +
"  SPDX-License-Identifier: Fair\n";

void print_credits() {
    set_style(TITLE_INK);
    write("%s\t", CREDITS);
    set_style(DEFAULT_INK);
}

constant INSTRUCTIONS =
"Hammurabi is a simulation game in which you, as the ruler of the ancient\n" +
"kingdom of Sumeria, Hammurabi, manage the resources.\n" +
"\n" +
"You may buy and sell land with your neighboring city-states for bushels of\n" +
"grain ― the price will vary between %d and %d bushels per acre.  You also must\n" +
"use grain to feed your people and as seed to plant the next year's crop.\n" +
"\n" +
"You will quickly find that a certain number of people can only tend a certain\n" +
"amount of land and that people starve if they are not fed enough.  You also\n" +
"have the unexpected to contend with such as a plague, rats destroying stored\n" +
"grain, and variable harvests.\n" +
"\n" +
"You will also find that managing just the few resources in this game is not a\n" +
"trivial job.  The crisis of population density rears its head very rapidly.\n" +
"\n" +
"Try your hand at governing ancient Sumeria for a %d-year term of office.\n";

void print_instructions() {
    set_style(INSTRUCTIONS_INK);
    write(
        INSTRUCTIONS,
        MIN_HARVESTED_BUSHELS_PER_ACRE,
        MAX_HARVESTED_BUSHELS_PER_ACRE,
        YEARS
        );
    set_style(DEFAULT_INK);
}

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

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

void pause(void|string prompt ) {
    prompt = prompt || "> ";
    set_style(INPUT_INK);
    accept_string(prompt);
    set_style(DEFAULT_INK);
}

// Print the given prompt, accept a string from the user. If the whole typed
// string is a valid integer (excluding trailing and leading white spaces,
// i.e.: space, tab, newline, carriage return, form feed, vertical tab and all
// the white spaces defined in Unicode), then return the number; otherwise
// return the typed string.
//
int|string accept_integer(string prompt) {
    string s = String.trim(accept_string(prompt));
    int n = (int) s;
    if ((string) n == s) {
        return n;
    } else {
        return s;
    }
}

// Print the given prompt, accept a string from the user. If the whole typed
// string is a valid integer (excluding trailing and leading white spaces,
// i.e.: space, tab, newline, carriage return, form feed, vertical tab and all
// the white spaces defined in Unicode), then return the number; otherwise
// return -1.
//
int get_integer(string prompt) {
    set_style(INPUT_INK);
    int|string answer = accept_integer(prompt);
    set_style(DEFAULT_INK);
    if (stringp(answer)) {
        return -1;
    } else {
        return answer;
    }
}

// Random numbers {{{1
// =============================================================

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

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

// Return a string with the proper wording for `n` persons, using the given or
// default words for singular and plural forms.
//
string persons(int n, string|void singular, string|void plural) {

    singular = singular || "person";
    plural = plural || "people";

    switch (n) {
        case 0: return "nobody";
        case 1: return sprintf("one %s", singular);
        default: return sprintf("%d %s", n, plural);
    }

}

string ordinal_suffix(int n) {
    switch (n) {
    case 1: return "st";
    case 2: return "nd";
    case 3: return "rd";
    default: return "th";
    }
}

// Game {{{1
// =============================================================================

// Return a string with the description of the given year as the previous one.
//
string previous(int year) {
    if (year == 0) {
        return "the previous year";
    } else {
        return sprintf("your %d%s year", year, ordinal_suffix(year));
    }
}

void print_annual_report(int year) {
    clear_screen();
    set_style(SPEECH_INK);
    write("Hammurabi, I beg to report to you.\n");
    set_style(DEFAULT_INK);

    string year_text = previous(year);
    string persons_text = persons(dead);
    string infants_text = persons(infants);

    write(
        "\nIn %s, %s starved and %s %s born.\n",
        year_text,
        persons_text,
        infants_text,
        (infants > 1) ? "were" : "was"
    );

    population += infants;

    if (year > 0 && random(1.0) <= PLAGUE_CHANCE) {
        population = (int) (population / 2);
        set_style(WARNING_INK);
        write("A horrible plague struck!  Half the people died.\n");
        set_style(DEFAULT_INK);
    }

    write("The population is %d.\n", population);
    write("The city owns %d acres.\n", acres);
    write(
        "You harvested %d bushels (%d per acre).\n",
        bushels_harvested,
        bushels_harvested_per_acre
    );
    if (bushels_eaten_by_rats > 0) {
        write("The rats ate %d bushels.\n", bushels_eaten_by_rats);
    }
    write("You have %d bushels in store.\n", bushels_in_store);
    bushels_harvested_per_acre =
        (int) ((float) RANGE_OF_HARVESTED_BUSHELS_PER_ACRE * random(1.0)) +
        MIN_HARVESTED_BUSHELS_PER_ACRE;
    write("Land is trading at %d bushels per acre.\n\n", bushels_harvested_per_acre);
}

void say_bye() {
    set_style(DEFAULT_INK);
    write("\nSo long for now.\n\n");
}

void quit_game() {
    say_bye();
    exit(0);
}

void relinquish() {
    set_style(SPEECH_INK);
    write("\nHammurabi, I am deeply irritated and cannot serve you anymore.\n");
    write("Please, get yourself another steward!\n");
    set_style(DEFAULT_INK);
    quit_game();
}

void increase_irritation() {
    irritation += 1 + random(IRRITATION_STEP);
    if (irritation >= MAX_IRRITATION) {
        relinquish(); // this never returns
    }
}

void print_irritated(string adverb) {
    write("The steward seems %s irritated.\n", adverb);
}

void show_irritation() {
    if (irritation < IRRITATION_STEP * 2) {
        print_irritated("slightly");
    } else if (irritation < IRRITATION_STEP * 3) {
        print_irritated("quite");
    } else if (irritation < IRRITATION_STEP * 4) {
        print_irritated("very");
    } else {
        print_irritated("profoundly");
    }
}

// Print a message begging to repeat an ununderstandable input.
//
void beg_repeat() {
    increase_irritation(); // this may never return
    set_style(SPEECH_INK);
    write("I beg your pardon?  I did not understand your order.\n");
    set_style(DEFAULT_INK);
    show_irritation();
}

// Print a message begging to repeat a wrong input, because there's only `n`
// items of `name`.
//
void beg_think_again(int n, string name) {
    increase_irritation(); // this may never return
    set_style(SPEECH_INK);
    write("I beg your pardon?  You have only %d %s.  Now then…\n", n, name);
    set_style(DEFAULT_INK);
    show_irritation();
}

// Buy or sell land.
//
void trade() {

    int acres_to_buy = 0;
    int acres_to_sell = 0;

    while (true) {
        acres_to_buy = get_integer("How many acres do you wish to buy? (0 to sell): ");
        if (acres_to_buy < 0) {
            beg_repeat(); // this may never return
        } else {
            if (bushels_harvested_per_acre * acres_to_buy <= bushels_in_store) {
                break;
            } else {
                beg_think_again(bushels_in_store, "bushels of grain");
            }
        }
    }

    if (acres_to_buy != 0) {
        write("You buy %d acres.\n", acres_to_buy);
        acres += acres_to_buy;
        bushels_in_store -= bushels_harvested_per_acre * acres_to_buy;
        write("You now have %d acres and %d bushels.\n", acres, bushels_in_store);
    } else {
        while (true) {
            acres_to_sell = get_integer("How many acres do you wish to sell?: ");
            if (acres_to_sell < 0) {
                beg_repeat(); // this may never return
            } else {
                if (acres_to_sell < acres) {
                    break;
                } else {
                    beg_think_again(acres, "acres");
                }
            }
        }

        if (acres_to_sell > 0) {
            write("You sell %d acres.\n", acres_to_sell);
            acres -= acres_to_sell;
            bushels_in_store += bushels_harvested_per_acre * acres_to_sell;
            write("You now have %d acres and %d bushels.\n", acres, bushels_in_store);
        }
    }
}

// Feed the people.
//
void feed() {
    while (true) {
        bushels_to_feed_with = get_integer("How many bushels do you wish to feed your people with?: ");
        if (bushels_to_feed_with < 0) {
            beg_repeat(); // this may never return
        } else {
            // Trying to use more grain than is in silos?
            if (bushels_to_feed_with <= bushels_in_store) {
                break;
            } else {
                beg_think_again(bushels_in_store, "bushels of grain");
            }
        }
    }

    write("You feed your people with %d bushels.\n", bushels_to_feed_with);
    bushels_in_store -= bushels_to_feed_with;
    write("You now have %d bushels.\n", bushels_in_store);
}

// Seed the land.
//
void seed() {

    int acres_to_seed = 0;

    while (true) {
        acres_to_seed = get_integer("How many acres do you wish to seed?: ");
        if (acres_to_seed < 0) {
            beg_repeat(); // this may never return
            continue;
        }
        if (acres_to_seed == 0) {
            break;
        }

        // Trying to seed more acres than you own?
        if (acres_to_seed > acres) {
            beg_think_again(acres, "acres");
            continue;
        }

        string message = sprintf(
            "bushels of grain,\nand one bushel can seed %d acres",
            ACRES_A_BUSHEL_CAN_SEED
        );

        // Enough grain for seed?
        if ((int) (acres_to_seed / ACRES_A_BUSHEL_CAN_SEED) > bushels_in_store) {
            beg_think_again(bushels_in_store, message);
            continue;
        }

        // Enough people to tend the crops?
        if (acres_to_seed <= ACRES_A_PERSON_CAN_SEED * population) {
            break;
        }

        message = sprintf(
            "people to tend the fields,\nand one person can seed %d acres",
            ACRES_A_PERSON_CAN_SEED
        );

        beg_think_again(population, message);
    }

    int bushels_used_for_seeding = (acres_to_seed / ACRES_A_BUSHEL_CAN_SEED);
    write("You seed %d acres using %d bushels.\n", acres_to_seed, bushels_used_for_seeding);
    bushels_in_store -= bushels_used_for_seeding;
    write("You now have %d bushels.\n", bushels_in_store);

    // A bountiful harvest!
    bushels_harvested_per_acre = random_1_to_5();
    bushels_harvested = acres_to_seed * bushels_harvested_per_acre;
    bushels_in_store += bushels_harvested;
}

bool is_even(int n) {
    return n % 2 == 0;
}

void check_rats() {
    int rat_chance = random_1_to_5();
    bushels_eaten_by_rats = is_even(rat_chance) ? (int) (bushels_in_store / rat_chance) : 0;
    bushels_in_store -= bushels_eaten_by_rats;
}

// Set the variables to their values in the first year.
//
void init() {
    dead = 0;
    total_dead = 0;
    starved_people_percentage = 0;
    population = 95;
    infants = 5;
    acres = ACRES_PER_PERSON * (population + infants);
    bushels_harvested_per_acre = 3;
    bushels_harvested = acres * bushels_harvested_per_acre;
    bushels_eaten_by_rats = 200;
    bushels_in_store = bushels_harvested - bushels_eaten_by_rats;
    irritation = 0;
}

void print_result(Result r) {
    set_style(RESULT_INK);

    switch (r) {
        case VERY_GOOD:
            write("A fantastic performance!  Charlemagne, Disraeli and Jefferson combined could\n");
            write("not have done better!\n");
            break;
        case NOT_TOO_BAD:
            write("Your performance could have been somewat better, but really wasn't too bad at\n");
            write(
                "all. %d people would dearly like to see you assassinated, but we all have our\n",
                (int) (population * 0.8 * random(1.0))
            );
            write("trivial problems.\n");
            break;
        case BAD:
            write("Your heavy-handed performance smacks of Nero and Ivan IV.  The people\n");
            write("(remaining) find you an unpleasant ruler and, frankly, hate your guts!\n");
            break;
        case VERY_BAD:
            write("Due to this extreme mismanagement you have not only been impeached and thrown\n");
            write("out of office but you have also been declared national fink!!!\n");
    }

    set_style(DEFAULT_INK);
}

void print_final_report() {
    clear_screen();

    if (starved_people_percentage > 0) {
        write(
            "In your %d-year term of office, %d percent of the\n",
            YEARS,
            starved_people_percentage
        );
        write(
            "population starved per year on the average, i.e., a total of %d people died!\n\n",
            total_dead
        );
    }

    int acres_per_person = acres / population;
    write(
        "You started with %d acres per person and ended with %d.\n\n",
        ACRES_PER_PERSON,
        acres_per_person
    );

    if (starved_people_percentage > 33 || acres_per_person < 7) {
        print_result(VERY_BAD);
    } else if (starved_people_percentage > 10 || acres_per_person < 9) {
        print_result(BAD);
    } else if (starved_people_percentage > 3 || acres_per_person < 10) {
        print_result(NOT_TOO_BAD);
    } else {
        print_result(VERY_GOOD);
    }
}

void check_starvation(int year) {
    // How many people has been fed?
    int fed_people = (bushels_to_feed_with / BUSHELS_TO_FEED_A_PERSON);

    if (population > fed_people) {
        dead = population - fed_people;
        starved_people_percentage = ((year - 1) * starved_people_percentage + dead * 100 / population) / year;
        population -= dead;
        total_dead += dead;

        // Starve enough for impeachment?
        if (dead > (int) (0.45 * population)) {
            set_style(WARNING_INK);
            write("\nYou starved %d people in one year!!!\n\n", dead);
            set_style(DEFAULT_INK);
            print_result(VERY_BAD);
            quit_game();
        }
    }
}

void govern() {
    init();

    print_annual_report(0);

    for (int year = 1; year <= YEARS; year += 1) {
        trade();
        feed();
        seed();
        check_rats();

        // Let's have some babies
        infants = (int) (random_1_to_5() * (20 * acres + bushels_in_store) / population / 100 + 1);

        check_starvation(year);

        pause("\nPress the Enter key to read the annual report. ");
        print_annual_report(year);
    }
}

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

void main() {
    clear_screen();
    print_credits();
    pause("\nPress the Enter key to read the instructions. ");
    clear_screen();
    print_instructions();
    pause("\nPress the Enter key to start. ");
    govern();
    pause("Press the Enter key to read the final report. ");
    print_final_report();
    say_bye();
}

High Noon

#!/usr/bin/env pike

// High Noon

// Original version in BASIC:
//  Designed and programmed by Chris Gaylo, Syosset High School, New York, 1970-09-12.
//  http://mybitbox.com/highnoon-1970/
//  http://mybitbox.com/highnoon/

// Transcriptions:
//  https://github.com/MrMethor/Highnoon-BASIC/
//  https://github.com/mad4j/basic-highnoon/

// Version modified for QB64:
//  By Daniele Olmisani, 2014.
//  https://github.com/mad4j/basic-highnoon/

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

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

void hide_cursor() {
    write("\x1B[?25l");
}

void show_cursor() {
    write("\x1B[?25h");
}

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();
}

// Global variables and constants {{{1
// =============================================================

constant DEFAULT_INK = FOREGROUND + WHITE;
constant INPUT_INK = FOREGROUND + BRIGHT + GREEN;
constant INSTRUCTIONS_INK = FOREGROUND + YELLOW;
constant TITLE_INK = FOREGROUND + BRIGHT + RED;

constant INITIAL_DISTANCE = 100;
constant INITIAL_BULLETS = 4;
constant MAX_WATERING_TROUGHS = 3;

int distance = 0; // distance between both gunners, in paces

int player_bullets = 0;
int opponent_bullets = 0;

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

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

// Print the given prompt and wait until the user enters a string.
//
string get_string(string prompt) {
    set_style(INPUT_INK);
    string s = accept_string(prompt);
    set_style(DEFAULT_INK);
    return s;
}

int accept_integer(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

// Print the given prompt and wait until the user enters an integer.
//
int get_integer(string prompt) {
    set_style(INPUT_INK);
    int n = accept_integer(prompt);
    set_style(DEFAULT_INK);
    return n;
}

// Return `true` if the given string is "yes" or a synonym.
//
bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

// Return `true` if the given string is "no" or a synonym.
//
bool is_no(string s) {
    switch(lower_case(s)) {
        case "n":
        case "no":
        case "nope":
            return true;
        default:
            return false;
    }
}

// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
bool yes(string prompt) {
    while (true) {
        string answer = get_string(prompt);
        if (is_yes(answer)) {
            return true;
        }
        if (is_no(answer)) {
            return false;
        }
    }
}

// Title, instructions and credits {{{1
// =============================================================

void print_title() {
    set_style(TITLE_INK);
    write("High Noon\n");
    set_style(DEFAULT_INK);
}

void print_credits() {
    print_title();
    write("\nOriginal version in BASIC:\n");
    write("    Designed and programmend by Chris Gaylo, 1970.\n");
    write("    http://mybitbox.com/highnoon-1970/\n");
    write("    http://mybitbox.com/highnoon/\n");
    write("Transcriptions:\n");
    write("    https://github.com/MrMethor/Highnoon-BASIC/\n");
    write("    https://github.com/mad4j/basic-highnoon/\n");
    write("Version modified for QB64:\n");
    write("    By Daniele Olmisani, 2014.\n");
    write("    https://github.com/mad4j/basic-highnoon/\n");
    write("This improved remake in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n");
}

void print_instructions() {
    print_title();
    set_style(INSTRUCTIONS_INK);
    write("\nYou have been challenged to a showdown by Black Bart, one of\n");
    write("the meanest desperadoes west of the Allegheny mountains.\n");
    write("\nWhile you are walking down a dusty, deserted side street,\n");
    write("Black Bart emerges from a saloon one hundred paces away.\n");
    write("\nBy agreement, you each have %d bullets in your six-guns.", INITIAL_BULLETS);
    write("\nYour marksmanship equals his. At the start of the walk nei-\n");
    write("ther of you can possibly hit the other, and at the end of\n");
    write("the walk, neither can miss. the closer you get, the better\n");
    write("your chances of hitting black Bart, but he also has beter\n");
    write("chances of hitting you.\n");
    set_style(DEFAULT_INK);
}

// Game loop {{{1
// =============================================================

string plural_suffix(int n) {
    switch (n) {
        case 1: return "";
        default: return "s";
    }
}

void print_shells_left() {
    if (player_bullets == opponent_bullets) {
        write("Both of you have %d bullets.\n", player_bullets);
    } else {
        write(
            "You now have %d bullet%s to Black Bart's %d bullet%s.\n",
            player_bullets,
            plural_suffix(player_bullets),
            opponent_bullets,
            plural_suffix(opponent_bullets));
    }
}

void print_check() {
    write("******************************************************\n");
    write("*                                                    *\n");
    write("*                 BANK OF DODGE CITY                 *\n");
    write("*                  CASHIER'S RECEIT                  *\n");
    write("*                                                    *\n");
    write("* CHECK NO. %04d                   AUGUST %dTH, 1889 *\n",
        random(1000),
        10 + random(10));
    write("*                                                    *\n");
    write("*                                                    *\n");
    write("*       PAY TO THE BEARER ON DEMAND THE SUM OF       *\n");
    write("*                                                    *\n");
    write("* TWENTY THOUSAND DOLLARS-------------------$20,000  *\n");
    write("*                                                    *\n");
    write("******************************************************\n");
}

void get_reward() {
    write("As mayor of Dodge City, and on behalf of its citizens,\n");
    write("I extend to you our thanks, and present you with this\n");
    write("reward, a check for $20,000, for killing Black Bart.\n\n\n");
    print_check();
    write("\n\nDon't spend it all in one place.\n");
}

void move_the_opponent() {
    int paces = 2 + random(8);
    write("Black Bart moves %d paces.\n", paces);
    distance -= paces;
}

// Maybe move the opponent; if so, return `true`, otherwise return `false`. A
// true `silent` flag allows to omit the message when the opponent doesn't
// move.
//
bool maybe_move_the_opponent(bool silent) {
    if (random(2) == 0) { // 50% chances
        move_the_opponent();
        return true;
    } else {
        if (!silent) {
            write("Black Bart stands still.\n");
        }
        return false;
    }
}

bool missed_shot() {
    return random(1.0) * 10.0 <= (distance / 10);
}

// Handle the opponent's shot and return a flag with the result: if the
// opponent kills the player, return `true`; otherwise return `false`.
//
bool the_opponent_fires_and_kills(string player_strategy) {
    write("Black Bart fires…\n");
    opponent_bullets -= 1;
    if (missed_shot()) {
        write("A miss…\n");
        switch (opponent_bullets) {
            case 3:
                write("Whew, were you lucky. That bullet just missed your head.\n");
                break;
            case 2:
                write("But Black Bart got you in the right shin.\n");
                break;
            case 1:
                write("Though Black Bart got you on the left side of your jaw.\n");
                break;
            case 0:
                write("Black Bart must have jerked the trigger.\n");
        }
    } else {
        if (player_strategy == "j") {
            write("That trick just saved yout life. Black Bart's bullet\n");
            write("was stopped by the wood sides of the trough.\n");
        } else {
            write("Black Bart shot you right through the heart that time.\n");
            write("You went kickin' with your boots on.\n");
            return true;
        }
    }
    return false;
}

// Handle the opponent's strategy and return a flag with the result: if the
// opponent runs or kills the player, return `true`; otherwise return `false`.
//
bool the_opponent_kills_or_runs(string player_strategy) {
    if (distance >= 10 || player_bullets == 0) {
        if (maybe_move_the_opponent(true)) {
            return false;
        }
    }
    if (opponent_bullets > 0) {
        return the_opponent_fires_and_kills(player_strategy);
    } else {
        if (player_bullets > 0) {
            if (random(2) == 0) { // 50% chances
                write("Now is your chance, Black Bart is out of bullets.\n");
            } else {
                write("Black Bart just hi-tailed it out of town rather than face you\n");
                write("without a loaded gun. You can rest assured that Black Bart\n");
                write("won't ever show his face around this town again.\n");
                return true;
            }
        }
    }
    return false;
}

void play() {

    distance = INITIAL_DISTANCE;
    int watering_troughs = 0;
    player_bullets = INITIAL_BULLETS;
    opponent_bullets = INITIAL_BULLETS;

    showdown: while (true) {

        write("You are now %d paces apart from Black Bart.\n", distance);
        print_shells_left();
        set_style(INSTRUCTIONS_INK);
        write("\nStrategies:\n");
        write("  [A]dvance\n");
        write("  [S]tand still\n");
        write("  [F]ire\n");
        write("  [J]ump behind the watering trough\n");
        write("  [G]ive up\n");
        write("  [T]urn tail and run\n");
        set_style(DEFAULT_INK);

        string player_strategy = lower_case(get_string("What is your strategy? "));

        switch (player_strategy) {

            case "a": // advance

                while (true) {
                    int paces = get_integer("How many paces do you advance? ");
                    if (paces < 0) {
                        write("None of this negative stuff, partner, only positive numbers.\n");
                    } else if (paces > 10) {
                        write("Nobody can walk that fast.\n");
                    } else {
                        distance -= paces;
                        break;
                    }
                }
                break;

            case "s": // stand still

                write("That move made you a perfect stationary target.\n");
                break;

            case "f": // fire

                if (player_bullets == 0) {

                    write("You don't have any bullets left.\n");

                } else {

                    player_bullets -= 1;
                    if (missed_shot()) {
                        switch (player_bullets) {
                            case 2:
                                write("Grazed Black Bart in the right arm.\n");
                                break;
                            case 1:
                                write("He's hit in the left shoulder, forcing him to use his right\n");
                                write("hand to shoot with.\n");
                        }
                        write("What a lousy shot.\n");
                        if (player_bullets == 0) {
                            write("Nice going, ace, you've run out of bullets.\n");
                            if (opponent_bullets != 0) {
                                write("Now Black Bart won't shoot until you touch noses.\n");
                                write("You better think of something fast (like run).\n");
                            }
                        }
                    } else {
                        write("What a shot, you got Black Bart right between the eyes.\n");
                        accept_string("\nPress the Enter key to get your reward. ");
                        clear_screen();
                        get_reward();
                        break showdown;
                    }

                }
                break;

            case "j": // jump

                if (watering_troughs == MAX_WATERING_TROUGHS) {
                    write("How many watering troughs do you think are on this street?\n");
                    player_strategy = "";
                } else {
                    watering_troughs += 1;
                    write("You jump behind the watering trough.\n");
                    write("Not a bad maneuver to threw Black Bart's strategy off.\n");
                }
                break;

            case "g": // give up

                write("Black Bart accepts. The conditions are that he won't shoot you\n");
                write("if you take the first stage out of town and never come back.\n");
                if (yes("Agreed? ")) {
                    write("A very wise decision.\n");
                    break showdown;
                } else {
                    write("Oh well, back to the showdown.\n");
                }
                break;

            case "t": // turn tail and run

                // The more bullets of the opponent, the less chances to escape.
                if (random((opponent_bullets + 2)) == 0) {
                    write("Man, you ran so fast even dogs couldn't catch you.\n");
                } else {
                    switch (opponent_bullets) {
                        case 0:
                            write("You were lucky, Black Bart can only throw his gun at you, he\n");
                            write("doesn't have any bullets left. You should really be dead.\n");
                            break;
                        case 1:
                            write("Black Bart fires his last bullet…\n");
                            write("He got you right in the back. That's what you deserve, for running.\n");
                            break;
                        case 2:
                            write("Black Bart fires and got you twice: in your back\n");
                            write("and your ass. Now you can't even rest in peace.\n");
                            break;
                        case 3:
                            write("Black Bart unloads his gun, once in your back\n");
                            write("and twice in your ass. Now you can't even rest in peace.\n");
                            break;
                        case 4:
                            write("Black Bart unloads his gun, once in your back\n");
                            write("and three times in your ass. Now you can't even rest in peace.\n");
                    }
                    opponent_bullets = 0;
                }
                break showdown;

            default:

                write("You sure aren't going to live very long if you can't even follow directions.\n");

        } // strategy switch

        if (the_opponent_kills_or_runs(player_strategy)) {
            break;
        }

        if (player_bullets + opponent_bullets == 0) {
            write("The showdown must end, because nobody has bullets left.\n");
            break;
        }

        write("\n");

    } // showdown loop

}

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

void main() {
    clear_screen();
    print_credits();
    accept_string("\nPress the Enter key to read the instructions. ");
    clear_screen();
    print_instructions();
    accept_string("\nPress the Enter key to start. ");
    clear_screen();
    play();
}

Math

#! /usr/bin/env pike

// Math

// Original version in BASIC:
//  Example included in Vintage BASIC 1.0.3.
//  http://www.vintage-basic.net

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

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

float accept_float(string prompt) {
    // NB The casting accepts any valid float at the start of the string
    // and ignores the rest;  if no valid float is found at the start of
    // the string, zero is returned.
    return (float) accept_string(prompt);
}

float accept_valid_float(string prompt) {
    float n = 0;
    while (n <= 0) {
        n = accept_float(prompt);
        if (n <= 0) {
            write("Float greater than zero expected.\n");
        }
    }
    return n;
}


void main() {

    float n = accept_valid_float("Enter a number: ");

    write("ABS(%[0]f) -> abs(%[0]f) -> %[1]f\n", n, abs(n));
    write("ATN(%[0]f) -> atan(%[0]f) -> %[1]f\n", n, atan(n));
    write("COS(%[0]f) -> cos(%[0]f) -> %[1]f\n", n, cos(n));
    write("EXP(%[0]f) -> exp(%[0]f) -> %[1]f\n", n, exp(n));
    write("INT(%[0]f) -> (int) (%[0]f) -> %[1]d\n", n, (int) n);
    write("LOG(%[0]f) -> log(%[0]f) -> %[1]f\n", n, log(n));
    write("SGN(%[0]f) -> sgn(%[0]f) -> %[1]d\n", n, sgn(n));
    write("SQR(%[0]f) -> sqrt(%[0]f) -> %[1]f\n", n, sqrt(n));
    write("TAN(%[0]f) -> tan(%[0]f) -> %[1]f\n", n, tan(n));

}

Mugwump

#!/usr/bin/env pike

// Mugwump

// Original version in BASIC:
//  Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).
//  Slightly modified by Bob Albrecht of People's Computer Company.
//  Published by Creative Computing (Morristown, New Jersey, USA), 1978.
//  - https://www.atariarchives.org/basicgames/showpage.php?page=114
//  - http://vintage-basic.net/games.html

// This version in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written in 2025-03-11/12.
//
// Last modified: 20250731T1954+0200.

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

constant NORMAL_STYLE = 0;

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

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();
}

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

constant GRID_SIZE = 10;
constant TURNS = 10;
constant MUGWUMPS = 4;

class Mugwump {
    int x;
    int y;
    bool hidden;
}

array(Mugwump) mugwump = allocate(MUGWUMPS);
int found = 0; // counter

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

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

// Print the given prompt, accept a string from the user. If the whole typed
// string is a valid integer (excluding trailing and leading white spaces,
// i.e.: space, tab, newline, carriage return, form feed, vertical tab and all
// the white spaces defined in Unicode), then return the number; otherwise
// return the typed string.
//
int|string accept_integer(string prompt) {
    string s = String.trim(accept_string(prompt));
    int n = (int) s;
    if ((string) n == s) {
        return n;
    } else {
        return s;
    }
}

int prompted_valid_integer(string prompt) {
    while (true) {
        int|string answer = accept_integer(prompt);
        if (stringp(answer)) {
            write("Integer expected.\n");
        } else {
            return answer;
        }
    }
}

bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

bool is_no(string s) {
    switch(lower_case(s)) {
        case "n":
        case "no":
        case "nope":
            return true;
        default:
            return false;
    }
}

// Print the given prompt, wait until the user enters a valid yes/no string;
// and return `true` for "yes" or `false` for "no".
//
bool yes(string prompt) {
    while (true) {
        string answer = accept_string(prompt);
        if (is_yes(answer)) {
            return true;
        }
        if (is_no(answer)) {
            return false;
        }
    }
}

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

// Clear the screen, print the credits and ask the user to press enter.
//
void print_credits() {
    clear_screen();
    write("Mugwump\n\n");
    write("Original version in BASIC:\n");
    write("    Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).\n");
    write("    Slightly modified by Bob Albrecht of People's Computer Company.\n");
    write("    Published by Creative Computing (Morristown, New Jersey, USA), 1978.\n");
    write("    - https://www.atariarchives.org/basicgames/showpage.php?page=114\n");
    write("    - http://vintage-basic.net/games.html\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. ");
}

// Clear the screen, print the instructions and ask the user to press enter.
//
void print_instructions() {
    clear_screen();
    write("Mugwump\n\n");
    write("The object of this game is to find four mugwumps\n");
    write("hidden on a 10 by 10 grid.  Homebase is position 0,0.\n");
    write("Any guess you make must be two numbers with each\n");
    write("number between 0 and 9, inclusive.  First number\n");
    write("is distance to right of homebase and second number\n");
    write("is distance above homebase.\n\n");
    write("You get %d tries.  After each try, you will see\n", TURNS);
    write("how far you are from each mugwump.\n\n");
    accept_string("Press Enter to start. ");
}

// Game {{{1
// =============================================================================

void hide_mugwumps() {
    for (int m = 0; m < MUGWUMPS; m += 1) {
        mugwump[m]->x = random(GRID_SIZE);
        mugwump[m]->y = random(GRID_SIZE);
        mugwump[m]->hidden = true;
    }
    found = 0; // counter
}

// Print the given prompt, wait until the user enters a valid coord and return
// it.
//
int get_coord(string prompt) {
    int coord = 0;
    while (true) {
        coord = prompted_valid_integer(prompt);
        if (coord < 0 || coord >= GRID_SIZE) {
            write("Invalid value %d: not in range [0, %d].\n", coord, GRID_SIZE - 1);
        } else {
            break;
        }
    }
    return coord;
}

// Return `true` if the given mugwump is hidden in the given coords.
//
bool is_here(int m, int x, int y) {
    return mugwump[m]->hidden && mugwump[m]->x == x && mugwump[m]->y == y;
}

// Return the distance between the given mugwump and the given coords.
//
int distance(int m, int x, int y) {
    return (int) sqrt(
        pow((mugwump[m]->x - x), 2.0) +
        pow((mugwump[m]->y - y), 2.0));
}

// Return a plural suffix (default: "s") if the given number is greater than 1
// otherwise return a singular suffix (default: an empty string).
//
string plural(int n, string|void plural_suffix, string|void singular_suffix) {
    plural_suffix = plural_suffix || "s";
    singular_suffix = singular_suffix || "";
    return n > 1 ? plural_suffix : singular_suffix;
}

// Run the game.
//
void play() {
    int x = 0;
    int y = 0;
    int turn = 0; // counter

    while (true) { // game

        clear_screen();
        hide_mugwumps();

        turns_loop: for (int turn = 1; turn <= TURNS; turn += 1) {
            write("Turn number %d\n\n", turn);
            write("What is your guess (in range [0, %d])?\n", GRID_SIZE - 1);
            x = get_coord("Distance right of homebase (x-axis): ");
            y = get_coord("Distance above homebase (y-axis): ");
            write("\nYour guess is (%d, %d).\n", x, y);

            for (int m = 0; m < MUGWUMPS; m += 1) {
                if (is_here(m, x, y)) {
                    mugwump[m]->hidden = false;
                    found += 1;
                    write("You have found mugwump %d!\n", m);
                    if (found == MUGWUMPS) {
                        break turns_loop;
                    }
                }
            }

            for (int m = 0; m < MUGWUMPS; m += 1) {
                if (mugwump[m]->hidden) {
                    write("You are %d units from mugwump %d.\n", distance(m, x, y) , m);
                }
            }
            write("\n");
        } // turns

        if (found == MUGWUMPS) {
            write("\nYou got them all in %d turn%s!\n\n", turn, plural(turn));
            write("That was fun! let's play again…\n");
            write("Four more mugwumps are now in hiding.\n");
        } else {
            write("\nSorry, that's %d tr%s.\n\n", TURNS, plural(TURNS, "ies", "y"));
            write("Here is where they're hiding:\n");
            for (int m = 0; m < MUGWUMPS; m += 1) {
                if (mugwump[m]->hidden) {
                    write("Mugwump %d is at (%d, %d).\n", m, mugwump[m]->x, mugwump[m]->y);
                }
            }
        }

        if (!yes("\nDo you want to play again? ")) {
            break;
        }
    } // game
}

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

void init() {
    // Build the `mugwump` array.
    for (int i = 0; i < MUGWUMPS; i += 1) {
        mugwump[i] = Mugwump();
    }
}

void main() {
    print_credits();
    print_instructions();
    init();
    play();
}

Name

#! /usr/bin/env pike

// Name

// Original version in BASIC:
//  Example included in Vintage BASIC 1.0.3.
//  http://www.vintage-basic.net

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

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

int accept_integer(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

int accept_valid_integer(string prompt) {
    int n = 0;
    while (n <= 0) {
        n = accept_integer(prompt);
        if (n <= 0) {
            write("Integer greater than zero expected.\n");
        }
    }
    return n;
}

void main() {
    string name = accept_string("What is your name? ");
    int number = accept_valid_integer("Enter a number: ");
    for (int i = 0; i < (number); i += 1) {
        write("Hello, %s!\n", name);
    }
}

Poetry

#! /usr/bin/env pike

// Poetry

// Original version in BASIC:
//  Unknown author.
//  Modified and reworked by Jim Bailey, Peggy Ewing, and Dave Ahl at DEC.
//  Published in "BASIC Computer Games", Creative Computing (Morristown, New Jersey, USA), 1978.
//  https://archive.org/details/Basic_Computer_Games_Microcomputer_Edition_1978_Creative_Computing
//  https://github.com/chaosotter/basic-games/tree/master/games/BASIC%20Computer%20Games/Poetry
//  http://vintage-basic.net/games.html

// This improved remake in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2025-03-08.
//
// Last modified: 20250310T0302+0100.

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

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();
}

constant DEFAULT_INK = FOREGROUND + WHITE;
constant INPUT_INK = FOREGROUND + BRIGHT + GREEN;
constant TITLE_INK = FOREGROUND + BRIGHT + RED;

// Title and credits {{{1
// =============================================================================

void print_title() {
    set_style(TITLE_INK);
    write("Poetry\n");
    set_style(DEFAULT_INK);
}

void print_credits() {
    print_title();
    write("\nOriginal version in BASIC:\n");
    write("    Unknown author.\n");
    write("    Published in \"BASIC Computer Games\",\n");
    write("    Creative Computing (Morristown, New Jersey, USA), 1978.\n\n");
    write("This improved remake in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n");
}

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

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

bool is_even(int n) {
    return n % 2 == 0;
}

void play() {

    int MAX_PHRASES_AND_VERSES = 20;

    // counters:
    int action = 0;
    int phrase = 0;
    int phrases_and_verses = 0;
    int verse_chunks = 0;

    verse: while (true) {

        bool manage_the_verse_continuation = true;
        bool maybe_add_comma = true;

        switch (action) {
            case 0:
            case 1:
                switch (phrase) {
                    case 0:
                        write("MIDNIGHT DREARY");
                        break;
                    case 1:
                        write("FIERY EYES");
                        break;
                    case 2:
                        write("BIRD OR FIEND");
                        break;
                    case 3:
                        write("THING OF EVIL");
                        break;
                    case 4:
                        write("PROPHET");
                }
                break;
            case 2:
                switch (phrase) {
                    case 0:
                        write("BEGUILING ME");
                        verse_chunks = 2;
                        break;
                    case 1:
                        write("THRILLED ME");
                        break;
                    case 2:
                        write("STILL SITTING…");
                        maybe_add_comma = false;
                        break;
                    case 3:
                        write("NEVER FLITTING");
                        verse_chunks = 2;
                        break;
                    case 4:
                        write("BURNED");
                }
                break;
            case 3:
                switch (phrase) {
                    case 0:
                        write("AND MY SOUL");
                        break;
                    case 1:
                        write("DARKNESS THERE");
                        break;
                    case 2:
                        write("SHALL BE LIFTED");
                        break;
                    case 3:
                        write("QUOTH THE RAVEN");
                        break;
                    case 4:
                        if (verse_chunks != 0) {
                            write("SIGN OF PARTING");
                        }
                }
                break;
            case 4:
                switch (phrase) {
                    case 0:
                        write("NOTHING MORE");
                        break;
                    case 1:
                        write("YET AGAIN");
                        break;
                    case 2:
                        write("SLOWLY CREEPING");
                        break;
                    case 3:
                        write("…EVERMORE");
                        break;
                    case 4: write("NEVERMORE");
                }
                break;
            case 5:
                action = 0;
                write("\n");
                if (phrases_and_verses > MAX_PHRASES_AND_VERSES) {
                    write("\n");
                    verse_chunks = 0;
                    phrases_and_verses = 0;
                    action = 2;
                    continue verse;
                } else {
                    manage_the_verse_continuation = false;
                }
        }

        if (manage_the_verse_continuation) {

            sleep(.25);

            if (maybe_add_comma && !(verse_chunks == 0 || random(1.0) > 0.19)) {
                write(",");
                verse_chunks = 2;
            }

            if (random(1.0) > 0.65) {
                write("\n");
                verse_chunks = 0;
            } else {
                write(" ");
                verse_chunks += 1;
            }

        }

        action += 1;
        phrase = random(5);
        phrases_and_verses += 1;

        if (!(verse_chunks > 0 || is_even(action))) {
            write("     ");
        }

    } // verse loop

}

void main() {
    clear_screen();
    print_credits();
    accept_string("\nPress the Enter key to start. ");
    clear_screen();
    play();
}

Russian Roulette

#! /usr/bin/env pike

// Russian Roulette

// Original version in BASIC:
//  Creative Computing (Morristown, New Jersey, USA), ca. 1980.

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

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

constant NORMAL_STYLE = 0;

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

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();
}

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

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

void press_enter_to_start() {
    accept_string("Press Enter to start. ");
}

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

void print_credits() {
    clear_screen();
    write("Russian Roulette\n\n");
    write("Original version in BASIC:\n");
    write("    Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n\n");
    press_enter_to_start();
}

void print_instructions() {
    clear_screen();
    write("Here is a revolver.\n");
    write("Type 'f' to spin chamber and pull trigger.\n");
    write("Type 'g' to give up, and play again.\n");
    write("Type 'q' to quit.\n\n");
}

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

void play() {

    int times = 0;

    while (true) { // game loop

        print_instructions();
        times = 0;

        play_loop: while (true) {

            string command = accept_string("> ");

            switch (command) {
                case "f": // fire
                    if (random(100) > 83) {
                        write("Bang! You're dead!\n");
                        write("Condolences will be sent to your relatives.\n");
                        break play_loop;
                    } else {
                        times += 1;
                        if (times > 10) {
                            write("You win!\n");
                            write("Let someone else blow his brains out.\n");
                            break play_loop;
                        } else {
                            write("Click.\n");
                        }
                    }
                    break;
                case "g": // give up
                    write("Chicken!\n");
                    break play_loop;
                case "q": // quit
                    return;
                default:
                    continue;
            }

        } // play loop

        press_enter_to_start();

    } // game loop

}

void bye() {
    write("Bye!\n");
}

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

Seance

#!/usr/bin/env pike

// Seance

// Original version in BASIC:
//  By Chris Oxlade, 1983.
//  https://archive.org/details/seance.qb64
//  https://github.com/chaosotter/basic-games

// This version in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2025-03-12.
//
// Last modified: 20250731T1954+0200.

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

void set_cursor_position(int y, int x) {
    write("\x1B[%d;%dH", y, x);
}

void hide_cursor() {
    write("\x1B[?25l");
}

void show_cursor() {
    write("\x1B[?25h");
}

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

void reset_attributes() {
    set_style(NORMAL_STYLE);
}

void erase_line_to_end() {
    write("\x1B[K");
}

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

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

// Config {{{1
// =============================================================================

constant TITLE = "Seance";

constant MAX_SCORE = 50;

constant MAX_MESSAGE_LENGTH = 6;
constant MIN_MESSAGE_LENGTH = 3;

constant BASE_CHARACTER = '@';
constant PLANCHETTE = '*';
constant SPACE = ' ';

constant FIRST_LETTER_NUMBER = 1;
constant LAST_LETTER_NUMBER = 26;

constant BOARD_INK = BRIGHT + CYAN + FOREGROUND;
constant DEFAULT_INK = WHITE + FOREGROUND;
constant INPUT_INK = BRIGHT + GREEN + FOREGROUND;
constant INSTRUCTIONS_INK = YELLOW + FOREGROUND;
constant MISTAKE_EFFECT_INK = BRIGHT + RED + FOREGROUND;
constant PLANCHETTE_INK = YELLOW + FOREGROUND;
constant TITLE_INK = BRIGHT + RED + FOREGROUND;

constant BOARD_X = 29; // screen column
constant BOARD_Y = 5; // screen line
constant BOARD_HEIGHT = 5; // characters displayed on the left and right borders
constant BOARD_WIDTH = 8; // characters displayed on the top and bottom borders
constant BOARD_PAD = 1; // blank characters separating the board from its left and right borders

constant BOARD_ACTUAL_WIDTH = BOARD_WIDTH + 2 * BOARD_PAD; // screen columns
constant BOARD_BOTTOM_Y = BOARD_HEIGHT + 1; // relative to the board

constant INPUT_X = BOARD_X;
constant INPUT_Y = BOARD_Y + BOARD_BOTTOM_Y + 4;

constant MESSAGES_Y = INPUT_Y;

constant MISTAKE_EFFECT_PAUSE = 3; // seconds

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

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

void press_enter(string prompt) {
    accept_string(prompt);
}

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

void print_title() {
    set_style(TITLE_INK);
    write("%s\n", TITLE);
    set_style(DEFAULT_INK);
}

void print_credits() {
    print_title();
    write("\nOriginal version in BASIC:\n");
    write("    Written by Chris Oxlade, 1983.\n");
    write("    https://archive.org/details/seance.qb64\n");
    write("    https://github.com/chaosotter/basic-games\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n");
}

void print_instructions() {
    print_title();
    set_style(INSTRUCTIONS_INK);
    write("\nMessages from the Spirits are coming through, letter by letter.  They want you\n");
    write("to remember the letters and type them into the computer in the correct order.\n");
    write("If you make mistakes, they will be angry -- very angry...\n");
    write("\n");
    write("Watch for stars on your screen -- they show the letters in the Spirits'\n");
    write("messages.\n");
    set_style(DEFAULT_INK);
}

// Game {{{1
// =============================================================================

int random_int_in_inclusive_range(int min, int max) {
    return random((max - min)) + min;
}

// Return the x coordinate to print the given text centered on the board.
//
int board_centered_x(string text) {
    return (BOARD_X + (BOARD_ACTUAL_WIDTH - sizeof(text)) / 2);
}

// Print the given text on the given row, centered on the board.
//
void print_centered(string text, int y) {
    set_cursor_position(y, board_centered_x(text));
    write("%s\n", text);
}

// Print the title on the given row, centered on the board.
//
void print_centered_title(int y) {
    set_style(TITLE_INK);
    print_centered(TITLE, y);
    set_style(DEFAULT_INK);
}

// Print the given letter at the given board coordinates.
//
void print_character(int y, int x, int char) {
    set_cursor_position(y + BOARD_Y, x + BOARD_X);
    write(sprintf("%c", char));
}

void print_board() {
    set_style(BOARD_INK);
    for (int i = 1; i <= BOARD_WIDTH; i += 1) {
        print_character(0, i + 1, BASE_CHARACTER + i); // top border
        print_character(BOARD_BOTTOM_Y, i + 1, BASE_CHARACTER + LAST_LETTER_NUMBER - BOARD_HEIGHT - i + 1); // bottom border
    }
    for (int i = 1; i <= BOARD_HEIGHT; i += 1) {
        print_character(i , 0, BASE_CHARACTER + LAST_LETTER_NUMBER - i + 1); // left border
        print_character(i , 3 + BOARD_WIDTH, BASE_CHARACTER + BOARD_WIDTH + i); // right border
    }
    write("\n");
    set_style(DEFAULT_INK);
}

void erase_line_from(int line, int column) {
    set_cursor_position(line, column);
    erase_line_to_end();
}

// Print the given mistake effect, wait a configured number of seconds and
// finally erase it.
//
void print_mistake_effect(string effect) {
    int x = board_centered_x(effect);
    hide_cursor();
    set_cursor_position(MESSAGES_Y, x);
    set_style(MISTAKE_EFFECT_INK);
    write("%s\n", effect);
    set_style(DEFAULT_INK);
    sleep(MISTAKE_EFFECT_PAUSE);
    erase_line_from(MESSAGES_Y, x);
    show_cursor();
}

// Return a new message of the given length, after marking its letters on the
// board.
//
string message(int length) {
    int y = 0;
    int x = 0;
    string letters = "";
    hide_cursor();
    for (int i = 0; i <= length; i += 1) {
        int letter_number = random_int_in_inclusive_range(
            FIRST_LETTER_NUMBER,
            LAST_LETTER_NUMBER
            );
        letters += sprintf("%c", BASE_CHARACTER + letter_number);
        if (letter_number <= BOARD_WIDTH) {
            // top border
            y = 1;
            x = letter_number + 1;
        } else if (letter_number <= BOARD_WIDTH + BOARD_HEIGHT) {
            // right border
            y = letter_number - BOARD_WIDTH;
            x = 2 + BOARD_WIDTH;
        } else if (letter_number <= BOARD_WIDTH + BOARD_HEIGHT + BOARD_WIDTH) {
            // bottom border
            y = BOARD_BOTTOM_Y - 1;
            x = 2 + BOARD_WIDTH + BOARD_HEIGHT + BOARD_WIDTH - letter_number;
        } else {
            // left border
            y = 1 + LAST_LETTER_NUMBER - letter_number;
            x = 1;
        }
        set_style(PLANCHETTE_INK);
        print_character(y, x, PLANCHETTE);
        set_style(DEFAULT_INK);
        sleep(1);
        print_character(y, x, SPACE);
    }
    show_cursor();
    return letters;

}

string accept_message() {
    set_style(INPUT_INK);
    set_cursor_position(INPUT_Y, INPUT_X);
    string result = upper_case(accept_string("? "));
    set_style(DEFAULT_INK);
    erase_line_from(INPUT_Y, INPUT_X);
    return result;
}

void play() {

    int score = 0;
    int mistakes = 0;

    print_centered_title(1);
    print_board();

    while (true) {
        int message_length = random_int_in_inclusive_range(
            MIN_MESSAGE_LENGTH,
            MAX_MESSAGE_LENGTH
            );
        string message_received = message(message_length);
        string message_understood = accept_message();
        if (message_received != message_understood) {
            mistakes += 1;
            switch (mistakes) {
                case 1:
                    print_mistake_effect("The table begins to shake!");
                    break;
                case 2:
                    print_mistake_effect("The light bulb shatters!");
                    break;
                case 3:
                    print_mistake_effect("Oh, no!  A pair of clammy hands grasps your neck!");
                    return;

            }
        } else {
            score += message_length;
            if (score >= MAX_SCORE) {
                print_centered("Whew!  The spirits have gone!", MESSAGES_Y);
                print_centered("You live to face another day!", MESSAGES_Y + 1);
                return;
            }
        }
    }
}

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

void main() {

    set_style(DEFAULT_INK);
    clear_screen();
    print_credits();

    press_enter("\nPress the Enter key to read the instructions. ");
    clear_screen();
    print_instructions();

    press_enter("\nPress the Enter key to start. ");
    clear_screen();
    play();
    write("\n");

}

Sine Wave

#! /usr/bin/env pike

// Sine Wave

// Original version in BASIC:
//  Creative Computing (Morristown, New Jersey, USA), ca. 1980.

// This version in Pike:
//  Copyright (c) 2024, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2024-12-17.
//
// Last modified: 20250313T0101+0100.

void clear_screen() {
    write("\x1B[0;0H\x1B[2J");
}

array(string) word = ({"", ""});

void set_words() {
    clear_screen();
    array(string) order = ({"first", "second"});
    for (int n = 0; n <= 1; n += 1) {
        while (word[n] == "") {
            write("Enter the " + order[n] + " word: ");
            word[n] = Stdio.stdin->gets();
        }
    }
}

void print_credits() {
    clear_screen();
    write("Sine Wave\n\n");
    write("Original version in BASIC:\n");
    write("    Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
    write("This version in Pike:\n");
    write("    Copyright (c) 2024, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n\n");
    write("Press Enter to start the program. ");
    Stdio.stdin->gets();
}

void draw() {
    clear_screen();
    bool even = false;
    for (float angle = 0.0; angle <= 40.0; angle += 0.25) {
        int spaces = (int) (26.0 + floor(25.0 * sin(angle)));
        for (int i = 0; i < spaces; i += 1)
            write(" ");
        write(word[even] + "\n");
        even = !even;
    }
}

void main() {
    print_credits();
    set_words();
    draw();
}

Slots

#!/usr/bin/env pike

// Slots
//  A slot machine simulation.

// Original version in BASIC:
//  Creative Computing (Morristown, New Jersey, USA).
//  Produced by Fred Mirabelle and Bob Harper on 1973-01-29.

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

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

void hide_cursor() {
    write("\x1B[?25l");
}

void show_cursor() {
    write("\x1B[?25h");
}

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();
}

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

constant REELS = 3;

array(string) image = ({" BAR  ", " BELL ", "ORANGE", "LEMON ", " PLUM ", "CHERRY"});
constant BAR = 0; // position of "BAR" in `image`.
array(int) color = ({
    FOREGROUND + WHITE,
    FOREGROUND + CYAN,
    FOREGROUND + YELLOW,
    FOREGROUND + BRIGHT + YELLOW,
    FOREGROUND + BRIGHT + WHITE,
    FOREGROUND + BRIGHT + RED });
constant MAX_BET = 100;
constant MIN_BET = 1;

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

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

int accept_integer(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

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

void print_credits() {
    clear_screen();
    write("Slots\n");
    write("A slot machine simulation.\n\n");
    write("Original version in BASIC:\n");
    write("    Creative computing (Morristown, New Jersey, USA).\n");
    write("    Produced by Fred Mirabelle and Bob Harper on 1973-01-29.\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 for instructions. ");
}

void print_instructions() {
    clear_screen();
    write("You are in the H&M casino, in front of one of our\n");
    write("one-arm bandits. Bet from %d to %d USD (or 0 to quit).\n\n",
        MIN_BET, MAX_BET);
    accept_string("Press Enter to start. ");
}

// Game {{{1
// =============================================================================

int won(int prize, int bet) {
    switch (prize) {
        case 2:
            write("DOUBLE!\n");
            break;
        case 5:
            write("*DOUBLE BAR*\n");
            break;
        case 10:
            write("**TOP DOLLAR**\n");
            break;
        case 100:
            write("***JACKPOT***\n");
    }
    write("You won!\n");
    return (prize + 1) * bet;
}

void show_standings(int usd) {
    write("Your standings are %d USD.\n", usd);
}

void print_reels(array(int) reel) {
    move_cursor_home();
    foreach (reel, int r) {
        set_style(color[r]);
        write("[%s] ", image[r]);
    }
    set_style(NORMAL_STYLE);
    write("\n");
}

void init_reels(array(int) reel) {
    int images = (sizeof(image));
    for (int i = 0; i < sizeof(reel); i += 1) {
        reel[i] = random(images);
    }
}

void spin_reels(array(int) reel) {
    constant DURATION_IN_SECONDS = 2;
    hide_cursor();
    int first_second = time();
    while (time() - first_second < DURATION_IN_SECONDS) {
        init_reels(reel);
        print_reels(reel);
    }
    show_cursor();
}

void play() {

    int standings = 0;
    int bet = 0;
    array(int) reel = allocate(REELS);
    int equals = 0;
    int bars = 0;

    init_reels(reel);

    play_loop: while (true) {

        bet_loop: while (true) {
            clear_screen();
            print_reels(reel);
            bet = accept_integer("Your bet (or 0 to quit): ");
            if (bet > MAX_BET) {
                write("House limits are %d USD.\n", MAX_BET);
                accept_string("Press Enter to try again. ");
            } else if (bet < MIN_BET) {
                string confirmation = accept_string("Type \"q\" to confirm you want to quit. ");
                if (confirmation == "q" || confirmation == "Q") {
                    break play_loop;
                }
            } else {
                break bet_loop;
            } // bet check

        } // bet loop

        clear_screen();
        spin_reels(reel);
        equals = sizeof(reel) - sizeof(Array.uniq(reel));
        equals += (equals > 0);
        bars = Array.count(reel, BAR);

        switch (equals) {
            case 3:
                if (bars == 3) {
                    standings += won(100, bet);
                } else {
                    standings += won(10, bet);
                }
                break;
            case 2:
                if (bars == 2) {
                    standings += won(5, bet);
                } else {
                    standings += won(2, bet);
                }
                break;
            default:
                write("You lost.\n");
                standings -= bet;
        } // prize check

        show_standings(standings);
        accept_string("Press Enter to continue. ");

    } // play loop

    show_standings(standings);

    if (standings < 0) {
        write("Pay up!  Please leave your money on the terminal.\n");
    } else if (standings > 0) {
        write("Collect your winnings from the H&M cashier.\n");
    } else {
        write("Hey, you broke even.\n");
    }

}

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

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

Stars

#! /usr/bin/env pike

// Stars

// Original version in BASIC:
//  Example included in Vintage BASIC 1.0.3.
//  http://www.vintage-basic.net

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

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

int accept_integer(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

int accept_valid_integer(string prompt) {
    int n = 0;
    while (n <= 0) {
        n = accept_integer(prompt);
        if (n <= 0) {
            write("Integer greater than zero expected.\n");
        }
    }
    return n;
}

bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

void main() {
    string name = accept_string("What is your name? ");
    write("Hello, %s.\n", name);
    do {
        int number = accept_valid_integer("How many stars do you want? ");
        write("%s\n", "*" * number);
    } while (is_yes(accept_string("Do you want more stars? ")));
}

Strings

#! /usr/bin/env pike

// Strings

// Original version in BASIC:
//  Example included in Vintage BASIC 1.0.3.
//  http://www.vintage-basic.net

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

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

int accept_integer(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

int accept_valid_integer(string prompt) {
    int n = 0;
    while (n <= 0) {
        n = accept_integer(prompt);
        if (n <= 0) {
            write("Integer greater than zero expected.\n");
        }
    }
    return n;
}

void main() {

    string s = accept_string("Enter a string: ");
    int n = accept_valid_integer("Enter an integer: ");

    write("ASC(\"%[0]s\") --> (int) \"%[0]s\"[0] --> %[1]d\n", s, (int) s[0]);
    write("CHR$(%[0]d) --> sprintf(\"%%c\", %[0]d) --> \"%[1]s\"\n", n, sprintf("%c", n));
    write("LEFT$(\"%[0]s\", %[1]d) --> \"%[0]s\"[..%[1]d - 1] --> \"%[2]s\"\n", s, n, s[..n - 1]);
    write("MID$(\"%[0]s\", %[1]d) --> \"%[0]s\"[%[1]d - 1..] --> \"%[2]s\"\n", s, n, s[n - 1..]);
    write("MID$(\"%[0]s\", %[1]d, 3) --> \"%[0]s\"[%[1]d - 1 .. %[1]d - 1 + 3 - 1] --> \"%[2]s\"\n", s, n, s[n - 1 .. n - 1 + 3 - 1]);
    write("RIGHT$(\"%[0]s\", %[1]d) --> \"%[0]s\"[sizeof(%[0]s) - %[1]d..] --> \"%[2]s\"\n", s, n, s[sizeof(s) - n..]);
    write("LEN(\"%[0]s\") --> sizeof(\"%[0]s\") --> %[1]d\n", s, sizeof(s));
    write("VAL(\"%[0]s\") --> (float) \"%[0]s\" --> %[1]f\n", s, (float) s);
    write("STR$(%[0]d) --> (string) %[0]d --> \"%[1]s\"\n", n, (string) n);
    write("SPC(%[0]d) --> \" \" * %[0]d -> \"%[1]s\"\n", n, " " * n);

}

Xchange

#!/usr/bin/env pike

// Xchange

// Original version in BASIC:
//  Written by Thomas C. McIntire, 1979.
//  Published in "The A to Z Book of Computer Games", 1979.
//  https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up
//  https://github.com/chaosotter/basic-games

// This improved remake in Pike:
//  Copyright (c) 2025, Marcos Cruz (programandala.net)
//  SPDX-License-Identifier: Fair
//
// Written on 2025-03-12.
//
// Last modified: 20250731T1954+0200.

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

constant BLACK = 0;
constant RED = 1;
constant GREEN = 2;
constant YELLOW = 3;
constant BLUE = 4;
constant MAGENTA = 5;
constant CYAN = 6;
constant WHITE = 7;
constant DEFAULT = 9;

constant STYLE_OFF = 20;
constant FOREGROUND = 30;
constant BACKGROUND = 40;
constant BRIGHT = 60;

constant NORMAL_STYLE = 0;

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

void set_cursor_position(int line, int column) {
    write("\x1B[%d;%dH", line, column);
}

void set_cursor_coord(array(int) coord) {
    set_cursor_position(coord[0], coord[1]);
}

void hide_cursor() {
    write("\x1B[?25l");
}

void show_cursor() {
    write("\x1B[?25h");
}

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

void reset_attributes() {
    set_style(NORMAL_STYLE);
}

void erase_line_to_end() {
    write("\x1B[K");
}

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

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

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

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

constant BOARD_INK = FOREGROUND + BRIGHT + CYAN;
constant DEFAULT_INK = FOREGROUND + WHITE;
constant INPUT_INK = FOREGROUND + BRIGHT + GREEN;
constant INSTRUCTIONS_INK = FOREGROUND + YELLOW;
constant TITLE_INK = FOREGROUND + BRIGHT + RED;

constant BLANK = "*";

constant GRID_HEIGHT = 3; // cell rows
constant GRID_WIDTH = 3; // cell columns

constant CELLS = GRID_WIDTH * GRID_HEIGHT;

array(string) pristine_grid = allocate(CELLS, "");

constant GRIDS_ROW = 3; // screen row where the grids are printed
constant GRIDS_COLUMN = 5; // screen column where the left grid is printed
constant CELLS_GAP = 2; // distance between the grid cells, in screen rows or columns
constant GRIDS_GAP = 16; // screen columns between equivalent cells of the grids

constant FIRST_PLAYER = 0;
constant MAX_PLAYERS = 4;

array(array(string)) grid = allocate(MAX_PLAYERS, allocate(CELLS, ""));

array(bool) is_playing = allocate(MAX_PLAYERS, false);

int players = 0;

constant QUIT_COMMAND = "X";

enum axis { Y, X }; // to use with coord arrays instead of 0 and 1, for clarity

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

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

int accept_integer_or_0(string prompt) {
    // NB The casting accepts any valid integer at the start of the string
    // and ignores the rest;  if no valid integer is found at the start of
    // the string, zero is returned.
    return (int) accept_string(prompt);
}

int get_integer(string|void prompt) {
    prompt = prompt || "";
    set_style(INPUT_INK);
    int n = accept_integer_or_0(prompt);
    set_style(DEFAULT_INK);
    return n;
}

string get_string(string|void prompt) {
    prompt = prompt || "";
    set_style(INPUT_INK);
    string s = accept_string(prompt);
    set_style(DEFAULT_INK);
    return s;
}

void press_enter(string prompt) {
    accept_string(prompt);
}

bool is_yes(string s) {
    switch(lower_case(s)) {
        case "ok":
        case "y":
        case "yeah":
        case "yes":
            return true;
        default:
            return false;
    }
}

bool is_no(string s) {
    switch(lower_case(s)) {
        case "n":
        case "no":
        case "nope":
            return true;
        default:
            return false;
    }
}

// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
bool yes(string prompt) {
    while (true) {
        string answer = get_string(prompt);
        if (is_yes(answer)) {
            return true;
        }
        if (is_no(answer)) {
            return false;
        }
    }
}

// Title, instructions and credits {{{1
// =============================================================

void print_title() {
    set_style(TITLE_INK);
    write("Xchange\n");
    set_style(DEFAULT_INK);
}

void print_credits() {
    print_title();
    write("\nOriginal version in BASIC:\n");
    write("    Written by Thomas C. McIntire, 1979.\n");
    write("    Published in \"The A to Z Book of Computer Games\", 1979.\n");
    write("    https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up\n");
    write("    https://github.com/chaosotter/basic-games\n");
    write("This improved remake in Pike:\n");
    write("    Copyright (c) 2025, Marcos Cruz (programandala.net)\n");
    write("    SPDX-License-Identifier: Fair\n");
}

void print_instructions() {
    print_title();
    set_style(INSTRUCTIONS_INK);
    write("\nOne or two may play.  If two, you take turns.  A grid looks like this:\n\n");
    set_style(BOARD_INK);
    write("    F G D\n");
    write("    A H %s\n", BLANK);
    write("    E B C\n\n");
    set_style(INSTRUCTIONS_INK);
    write("But it should look like this:\n\n");
    set_style(BOARD_INK);
    write("    A B C\n");
    write("    D E F\n");
    write("    G H %s\n\n", BLANK);
    set_style(INSTRUCTIONS_INK);
    write("You may exchange any one letter with the '%s', but only one that's adjacent:\n", BLANK);
    write("above, below, left, or right.  Not all puzzles are possible, and you may enter\n");
    write("'%s' to give up.\n\n", QUIT_COMMAND);
    write("Here we go...\n");
    set_style(DEFAULT_INK);
}

// Grids {{{1
// =============================================================

// Print the given player's grid title.
//
void print_grid_title(int player) {
    set_cursor_position(GRIDS_ROW, GRIDS_COLUMN + (player * GRIDS_GAP));
    write("Player %d", player + 1);
}

// Return the cursor position of the given player's grid cell.
//
array(int) cell_position(int player, int cell) {
    int grid_row = cell / GRID_HEIGHT;
    int grid_column = cell % GRID_WIDTH;
    int title_margin = players > 1 ? 2 : 0;
    int row = GRIDS_ROW + title_margin + grid_row;
    int column = GRIDS_COLUMN +
        (grid_column * CELLS_GAP) +
        (player * GRIDS_GAP);
    return ({ row, column });
}

// Return the cursor position of the given player's grid prompt.
//
array(int) grid_prompt_position_of(int player) {
    array(int) coord = cell_position(player, CELLS);
    int row = coord[0];
    int column = coord[1];
    return ({ row + 1, column });
}

// Print the given player's grid, in the given or default color.
//
void print_grid(int player, int|void color) {
    color = color || BOARD_INK;
    if (players > 1) {
        print_grid_title(player);
    }
    set_style(color);
    for (int cell = 0; cell < CELLS; cell += 1) {
        set_cursor_coord(cell_position(player, cell));
        write(grid[player][cell]);
    }
    set_style(DEFAULT_INK);
}

// Print the current players' grids.
//
void print_grids() {
    for (int player = 0; player < players; player += 1) {
        if (is_playing[player]) {
            print_grid(player);
        }
    }
    write("\n");
    erase_screen_to_end();
}

// Scramble the grid of the given player.
//
void scramble_grid(int player) {
    for (int cell = 0; cell < CELLS; cell += 1) {
        int random_cell = random(CELLS);
        // Exchange the contents of the current cell with that of the random one.
        string  tmp = grid[player][cell];
        grid[player][cell] = grid[player][random_cell];
        grid[player][random_cell] = tmp;
    }
}

// Init the grids.
//
void init_grids() {
    grid[0] = copy_value(pristine_grid);
    scramble_grid(0);
    for (int player = 1; player < players; player += 1) {
        grid[player] = copy_value(grid[0]);
    }
}

// Messages {{{1
// =============================================================

// Return a message prefix for the given player.
//
string player_prefix(int player) {
    return players > 1 ? sprintf("Player %d: ", player + 1) : "";
}

// Return the cursor position of the given player's messages, adding the given
// row increment, which defaults to zero.
//
array(int) message_position(int player, int|void row_inc) {
    row_inc = row_inc || 0;
    array(int) prompt_coord = grid_prompt_position_of(player);
    return ({ prompt_coord[Y] + 2 + row_inc, 1 });
}

// Print the given message about the given player, adding the given row
// increment, which defaults to zero, to the default cursor coordinates.
//
void print_message(string message, int player, int|void row_inc) {
    row_inc = row_inc || 0;
    set_cursor_coord(message_position(player, row_inc));
    write("%s%s", player_prefix(player), message);
    erase_line_to_end();
    write("\n");
}

// Erase the last message about the given player.
//
void erase_message(int player) {
    set_cursor_coord(message_position(player));
    erase_line_to_end();
}

// Game loop {{{1
// =============================================================

// Return a message with the players range.
//
string players_range_message() {
    if (MAX_PLAYERS == 2) {
        return "1 or 2";
    } else {
        return sprintf("from 1 to %d", MAX_PLAYERS);
    }
}

// Return the number of players, asking the user if needed.
//
int number_of_players() {
    int players = 0;
    print_title();
    write("\n");
    if (MAX_PLAYERS == 1) {
        players = 1;
    } else {
        while (players < 1 || players > MAX_PLAYERS) {
            string prompt = sprintf("Number of players (%s): ", players_range_message());
            players = get_integer(prompt);
        }
    }
    return players;
}

// Is the given cell the first one on a grid row?
//
bool is_first_cell_of_a_grid_row(int cell) {
    return cell % GRID_WIDTH == 0;
}

// Is the given cell the last one on a grid row?
//
bool is_last_cell_of_a_grid_row(int cell) {
    return (cell + 1) % GRID_WIDTH == 0;
}

// Are the given cells adjacent?
//
bool are_cells_adjacent(int cell_1, int cell_2) {
    return (cell_2 == cell_1 + 1 && !is_first_cell_of_a_grid_row(cell_2)) ||
        (cell_2 == cell_1 + GRID_WIDTH) ||
        (cell_2 == cell_1 - 1 && !is_last_cell_of_a_grid_row(cell_2)) ||
        (cell_2 == cell_1 - GRID_WIDTH);
}

// If the given player's character cell is a valid move, i.e. it is adjacent to
// the blank cell, return the blank cell; otherwise return an empty string.
//
int|string position_to_cell(int player, int char_cell) {
    for (int cell = 0; cell < CELLS; cell += 1) {
        if (grid[player][cell] == BLANK) {
            if (are_cells_adjacent(char_cell, cell)) {
                return cell;
            } else {
                break;
            }
        }
    }
    print_message(sprintf("Illegal move \"%s\".", grid[player][char_cell]), player);
    return "";
}

// If the given player's command is valid, i.e. a grid character, return its
// position; otherwise return an empty string.
//
int|string command_to_position(int player, string command) {
    if (command != BLANK) {
        for (int position = 0; position < CELLS; position += 1) {
            if (command == grid[player][position]) {
                return position;
            }
        }
    }
    print_message(sprintf("Invalid character \"%s\".", command), player);
    return "";
}

// Forget the given player, who quitted.
//
void forget_player(int player) {
    is_playing[player] = false;
    print_grid(player, DEFAULT_INK);
}

// Play the turn of the given player.
//
void play_turn(int player) {
    int blank_position = 0;
    int character_position = 0;

    if (is_playing[player]) {
        while (true) {
            while (true) {
                array(int) coord = grid_prompt_position_of(player);
                set_cursor_coord(coord);
                erase_line_to_end();
                set_cursor_coord(coord);
                string command = upper_case(String.trim(get_string("Move: ")));
                if (command == QUIT_COMMAND) {
                    forget_player(player);
                    return;
                }
                int|string position = command_to_position(player, command);
                if (intp(position)) {
                    character_position = position;
                    break;
                }
            }
            int|string position = position_to_cell(player, character_position);
            if (intp(position)) {
                blank_position = position;
                break;
            }
        }
        erase_message(player);
        grid[player][blank_position] = grid[player][character_position];
        grid[player][character_position] = BLANK;
    }
}

// Play the turns of all players.
//
void play_turns() {
    for (int player = 0; player < players; player += 1) {
        play_turn(player);
    }
}

// Is someone playing?
//
bool is_someone_playing() {
    for (int player = 0; player < players; player += 1) {
        if (is_playing[player]) {
            return true;
        }
    }
    return false;
}

bool has_an_empty_grid(int player) {
    for (int i = 0; i < CELLS; i += 1) {
        if (grid[player][i] != "") {
            return false;
        }
    }
    return true;
}

// Has someone won? If so, print a message for every winner and return `true`
// otherwise just return `false`.
//
bool has_someone_won() {
    int winners = 0;
    for (int player = 0; player < players; player += 1) {
        if (is_playing[player]) {
            if (has_an_empty_grid(player)) {
                winners += 1;
                if (winners > 0) {
                    print_message(
                        sprintf("You're the winner%s!", (winners > 1) ? ", too" : ""),
                        player,
                        winners - 1);
                }
            }
        }
    }
    return winners > 0;
}

// Init the game.
//
void init_game() {
    clear_screen();
    players = number_of_players();
    for (int player = 0; player < players; player += 1) {
        is_playing[player] = true;
    }
    clear_screen();
    print_title();
    init_grids();
    print_grids();
}

// Play the game.
//
void play() {
    init_game();
    while (is_someone_playing()) {
        play_turns();
        print_grids();
        if (has_someone_won()) {
            break;
        }
    }
}

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

void init_once() {

    // Init the pristine grid.
    constant FIRST_CHAR_CODE = 'A';
    for (int cell = 0; cell < CELLS - 1; cell += 1) {
        pristine_grid[cell] = sprintf("%c", FIRST_CHAR_CODE + cell);
    }
    pristine_grid[CELLS - 1] = BLANK;
}

// Return `true` if the player does not want to play another game; otherwise
// return `false`.
//
bool enough() {
    set_cursor_coord(grid_prompt_position_of(FIRST_PLAYER));
    return !yes("Another game? ");
}

void main() {
    init_once();

    clear_screen();
    print_credits();
    press_enter("\nPress the Enter key to read the instructions. ");

    clear_screen();
    print_instructions();
    press_enter("\nPress the Enter key to start. ");

    while (true) {
        play();
        if (enough()) {
            break;
        }
    }
    write("So long…\n");
}

Related pages

Basics off
Metaproject about the "Basics of…" projects.
Basics of 8th
Conversion of old BASIC programs to 8th in order to learn the basics of this language.
Basics of Ada
Conversion of old BASIC programs to Ada in order to learn the basics of this language.
Basics of Arturo
Conversion of old BASIC programs to Arturo in order to learn the basics of this language.
Basics of C#
Conversion of old BASIC programs to C# in order to learn the basics of this language.
Basics of C3
Conversion of old BASIC programs to C3 in order to learn the basics of this language.
Basics of Chapel
Conversion of old BASIC programs to Chapel in order to learn the basics of this language.
Basics of Clojure
Conversion of old BASIC programs to Clojure in order to learn the basics of this language.
Basics of Crystal
Conversion of old BASIC programs to Crystal in order to learn the basics of this language.
Basics of D
Conversion of old BASIC programs to D in order to learn the basics of this language.
Basics of Elixir
Conversion of old BASIC programs to Elixir in order to learn the basics of this language.
Basics of F#
Conversion of old BASIC programs to F# in order to learn the basics of this language.
Basics of Factor
Conversion of old BASIC programs to Factor in order to learn the basics of this language.
Basics of FreeBASIC
Conversion of old BASIC programs to FreeBASIC in order to learn the basics of this language.
Basics of Gleam
Conversion of old BASIC programs to Gleam in order to learn the basics of this language.
Basics of Go
Conversion of old BASIC programs to Go in order to learn the basics of this language.
Basics of Hare
Conversion of old BASIC programs to Hare in order to learn the basics of this language.
Basics of Haxe
Conversion of old BASIC programs to Haxe in order to learn the basics of this language.
Basics of Icon
Conversion of old BASIC programs to Icon in order to learn the basics of this language.
Basics of Io
Conversion of old BASIC programs to Io in order to learn the basics of this language.
Basics of Janet
Conversion of old BASIC programs to Janet in order to learn the basics of this language.
Basics of Julia
Conversion of old BASIC programs to Julia in order to learn the basics of this language.
Basics of Kotlin
Conversion of old BASIC programs to Kotlin in order to learn the basics of this language.
Basics of Lobster
Conversion of old BASIC programs to Lobster in order to learn the basics of this language.
Basics of Lua
Conversion of old BASIC programs to Lua in order to learn the basics of this language.
Basics of Nature
Conversion of old BASIC programs to Nature in order to learn the basics of this language.
Basics of Neat
Conversion of old BASIC programs to Neat in order to learn the basics of this language.
Basics of Neko
Conversion of old BASIC programs to Neko in order to learn the basics of this language.
Basics of Nelua
Conversion of old BASIC programs to Nelua in order to learn the basics of this language.
Basics of Nim
Conversion of old BASIC programs to Nim in order to learn the basics of this language.
Basics of Nit
Conversion of old BASIC programs to Nit in order to learn the basics of this language.
Basics of Oberon-07
Conversion of old BASIC programs to Oberon-07 in order to learn the basics of this language.
Basics of OCaml
Conversion of old BASIC programs to OCaml in order to learn the basics of this language.
Basics of Odin
Conversion of old BASIC programs to Odin in order to learn the basics of this language.
Basics of Pony
Conversion of old BASIC programs to Pony in order to learn the basics of this language.
Basics of Python
Conversion of old BASIC programs to Python in order to learn the basics of this language.
Basics of Racket
Conversion of old BASIC programs to Racket in order to learn the basics of this language.
Basics of Raku
Conversion of old BASIC programs to Raku in order to learn the basics of this language.
Basics of Retro
Conversion of old BASIC programs to Retro in order to learn the basics of this language.
Basics of Rexx
Conversion of old BASIC programs to Rexx in order to learn the basics of this language.
Basics of Ring
Conversion of old BASIC programs to Ring in order to learn the basics of this language.
Basics of Rust
Conversion of old BASIC programs to Rust in order to learn the basics of this language.
Basics of Scala
Conversion of old BASIC programs to Scala in order to learn the basics of this language.
Basics of Scheme
Conversion of old BASIC programs to Scheme in order to learn the basics of this language.
Basics of Styx
Conversion of old BASIC programs to Styx in order to learn the basics of this language.
Basics of Swift
Conversion of old BASIC programs to Swift in order to learn the basics of this language.
Basics of V
Conversion of old BASIC programs to V in order to learn the basics of this language.
Basics of Vala
Conversion of old BASIC programs to Vala in order to learn the basics of this language.
Basics of Zig
Conversion of old BASIC programs to Zig in order to learn the basics of this language.

External related links