Basics of Vala
Priskribo de la ĉi-paĝa enhavo
Konverto de malnovaj BASIC-programoj al Vala por lerni la fundamentojn de ĉi-tiu lingvo.
Etikedoj:
3D Plot
/*
3D Plot
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Vala:
Copyright (c) 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written in 2023-08-30/31.
Last modified 20260828T1048+0200.
*/
using GLib; // Math needed
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void reset_screen_attributes() {
stdout.printf("\x1B[0m");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_screen_attributes();
move_cursor_home();
}
void print_credits() {
stdout.printf("3D Plot\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Creative computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2023, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
stdout.printf("Press Enter to start the program.\n");
stdin.read_line();
}
double a(double z) {
return 30 * Math.exp(-z * z / 100);
}
void draw() {
const int width = 56;
const char space = ' ';
const char dot = '*';
char line[width];
int l = 0;
int z = 0;
int y1 = 0;
for (double x = -30.0; x <= 30.0; x += 1.5) {
for (int pos = 0; pos < width; pos++) {
line[pos] = space;
}
l = 0;
y1 = 5 * (int)(Math.sqrt(900 - x * x) / 5);
for (int y = y1; y >= -y1; y += -5) {
z = (int)(25 + a(Math.sqrt(x * x + (y * y))) - 0.7 * y);
if (z > l) {
l = z;
line[z] = dot;
}
} // y loop
for (int pos = 0; pos < width; pos++) {
stdout.printf(line[pos].to_string());
}
stdout.printf("\n");
} // x loop
}
void main() {
clear_screen();
print_credits();
clear_screen();
draw();
}
Bagels
// Bagels
// Original version in BASIC:
// D. Resek, P. Rowe, 1978.
// Creative Computing (Morristown, New Jersey, USA), 1978.
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-30.
//
// Last modified: 20260830T1340+0200.
// Terminal {{{1
// =============================================================================
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Credits and instructions {{{1
// =============================================================================
void print_credits() {
clear_screen();
stdout.printf("Bagels\n");
stdout.printf("Number guessing game\n\n");
stdout.printf("Original source unknown but suspected to be:\n");
stdout.printf(" Lawrence Hall of Science, U.C. Berkely.\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" D. Resek, P. Rowe, 1978.\n");
stdout.printf(" Creative computing (Morristown, New Jersey, USA), 1978.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
accept_string("Press Enter to read the instructions. ");
}
void print_instructions() {
clear_screen();
stdout.printf("Bagels\n");
stdout.printf("Number guessing game\n\n");
stdout.printf("I am thinking of a three-digit number that has no two digits the same.\n");
stdout.printf("Try to guess it and I will give you clues as follows:\n\n");
stdout.printf(" PICO - one digit correct but in the wrong position\n");
stdout.printf(" FERMI - one digit correct and in the right position\n");
stdout.printf(" BAGELS - no digits correct\n\n");
accept_string("Press Enter to start. ");
}
// User input {{{1
// =============================================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_yes(string s) {
switch (s.down()) {
case "ok":
case "y":
case "yeah":
case "yes":
return true;
default:
return false;
}
}
bool is_no(string s) {
switch (s.down()) {
case "n":
case "no":
case "nope":
return true;
default:
return false;
}
}
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
// =============================================================================
const int DIGITS = 3;
bool is_digit(int ascii_code) {
return (ascii_code >= '0') && (ascii_code <= '9');
}
int[] accept_input(string prompt)
ensures(result.length == DIGITS)
{
const char ASCII_0 = '0';
int[] user_digit = new int[DIGITS];
while (true) {
string input = accept_string(prompt);
if (input.length != DIGITS) {
stdout.printf("Remember it's a %d-digit number.\n", DIGITS);
continue;
}
{
bool error = false;
for (int pos = 0; pos < input.length; pos++) {
int digit = input[pos];
if (is_digit(digit)) {
user_digit[pos] = digit - ASCII_0;
} else {
stdout.printf("What?\n");
error = true;
break;
}
}
if (error) {
continue;
}
}
if (is_any_repeated(user_digit)) {
stdout.printf("Remember my number has no two digits the same.\n");
continue;
} else {
break;
}
}
return user_digit;
}
// Return three random digits.
int[] random_number() {
int[] random_digit = new int[DIGITS];
for (int i = 0; i < DIGITS; i++) {
bool found = true;
do {
random_digit[i] = GLib.Random.int_range(0, 10);
for (int j = 0; j < i; j++) {
if (i != j && random_digit[i] == random_digit[j]) {
found = false;
break;
}
}
} while (!found);
}
return random_digit;
}
bool is_any_repeated(int[] number)
requires(number.length == DIGITS)
{
for (int i = 0; i < DIGITS; i++) {
for (int j = i + 1; j < DIGITS; j++) {
if (number[i] == number[j]) {
return true;
}
}
}
return false;
}
string replicate(string s, int count) {
string result = "";
for (int i = 0; i < count; i++) {
result += s;
}
return result;
}
void play() {
const int TRIES = 20;
int score = 0;
int fermi = 0; // counter
int pico = 0; // counter
int[] computer_number = new int[DIGITS];
int[] user_number = new int[DIGITS];
while (true) {
clear_screen();
computer_number = random_number();
stdout.printf("O.K. I have a number in mind.\n");
for (int guess = 1; guess < TRIES + 1; guess++) {
user_number = accept_input(@"Guess #$guess: ");
fermi = 0;
pico = 0;
for (int i = 0; i < DIGITS; i++) {
for (int j = 0; j < DIGITS; j++) {
if (user_number[i] == computer_number[j]) {
if (i == j) {
fermi += 1;
}
else {
pico += 1;
}
}
}
}
if (pico + fermi == 0) {
stdout.printf("BAGELS\n");
}
else {
stdout.printf(
"%s%s\n",
replicate("PICO ", pico),
replicate("FERMI ", fermi)
);
if (fermi == DIGITS) {
break;
}
}
}
if (fermi == DIGITS) {
stdout.printf("You got it!!!\n");
score += 1;
}
else {
stdout.printf("Oh well.\n");
stdout.printf("That's %d guesses. My number was ", TRIES);
for (int i = 0; i < DIGITS; i++) {
stdout.printf(computer_number[i].to_string());
}
stdout.printf(".\n");
}
if (!yes("Play again? ")) {
break;
}
}
if (score != 0) {
stdout.printf("A %d-point bagels, buff!!\n", score);
}
stdout.printf("Hope you had fun. Bye.\n");
}
void main() {
print_credits();
print_instructions();
play();
}
Bug
/*
Bug
Original version in BASIC:
Brian Leibowitz, 1978.
Creative Computing (Morristown, New Jersey, USA), 1978.
This version in Vala:
Copyright (c) 2023, 2026, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written in 2023-10, 2023-12, 2026-08.
Last modified 20260831T0149+0200.
*/
using GLib;
struct bug_type {
bool body;
bool neck;
bool head;
int feelers;
char feeler_kind;
bool tail;
int legs;
bool finished;
}
struct player_type {
string pronoun;
string possessive;
bug_type bug;
}
player_type computer;
player_type human;
enum part_id { body = 1, neck, head, feeler, tail, leg }
const int body_height = 2;
const int feeler_length = 4;
const int leg_length = 2;
const int max_feelers = 2;
const int max_legs = 6;
const int neck_length = 2;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void reset_screen_attributes() {
stdout.printf("\x1B[0m");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_screen_attributes();
move_cursor_home();
}
// Move the cursor up by a number of rows, without changing the column
// position.
void move_cursor_up(int rows = 1) {
stdout.printf("\033[%iA", rows);
}
// Erase the current line, without moving the cursor position.
void erase_line() {
stdout.printf("\033[2K");
}
void erase_previous_line() {
move_cursor_up();
erase_line();
}
void print_credits() {
clear_screen();
stdout.printf("Bug\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Brian Leibowitz, 1978.\n");
stdout.printf(" Creative computing (Morristown, New Jersey, USA), 1978.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2023, 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
stdout.printf("Press Enter to read the instructions. ");
stdin.read_line();
}
string left_justify(string s, int width) {
return s + repeat(" ", width - s.length);
}
string repeat(string s, int times) {
string result = "";
for (int t = 0; t < times; t++) {
result = result + s;
}
return result;
}
string capitalized(string s) {
return
s.slice(0, 1).up()
+ (s.length > 1 ? s.slice(1, s.length) : "");
}
string part_name(part_id part) {
string result = "";
switch (part) {
case part_id.body:
result = "body";
break;
case part_id.neck:
result = "neck";
break;
case part_id.head:
result = "head";
break;
case part_id.feeler:
result = "feeler";
break;
case part_id.tail:
result = "tail";
break;
case part_id.leg:
result = "leg";
break;
}
return result;
}
void print_parts_table() {
const int columns = 3;
const int column_width = 8;
const int column_separation = 2;
// Headers
string[] header = {"Number", "Part", "Quantity"};
for (int i = 0; i < columns; i++) {
stdout.printf(left_justify(header[i], column_width + column_separation));
}
stdout.printf("\n");
// Rulers
for (int i = 0; i < columns; i++) {
stdout.printf(
(i == 0 ? "" : repeat(" ", column_separation))
+ repeat("-", column_width)
);
}
stdout.printf("\n");
// Data
int part_quantity[6];
part_quantity[part_id.body] = 1;
part_quantity[part_id.neck] = 1;
part_quantity[part_id.head] = 1;
part_quantity[part_id.feeler] = 2;
part_quantity[part_id.tail] = 1;
part_quantity[part_id.leg] = 6;
for (int part = part_id.body; part <= part_id.leg; part++) {
stdout.printf(
left_justify("%i".printf(part), column_width + column_separation)
+ left_justify(capitalized(part_name(part)), column_width + column_separation)
+ "%i".printf(part_quantity[part])
+ "\n"
);
}
}
const string instructions = """
The object is to finish your bug before I finish mine. Each number
stands for a part of the bug body.
I will roll the die for you, tell you what I rolled for you, what the
number stands for, and if you can get the part. If you can get the
part I will give it to you. The same will happen on my turn.
If there is a change in either bug I will give you the option of
seeing the pictures of the bugs. The numbers stand for parts as
follows:
""";
void print_instructions() {
clear_screen();
stdout.printf("Bug\n");
stdout.printf(instructions);
print_parts_table();
stdout.printf("\nPress Enter to start. ");
stdin.read_line();
}
void print_feelers(bug_type bug) {
for (int i = 0; i < feeler_length; i++) {
stdout.printf(" ");
for (int j = 0; j < bug.feelers; j++) {
stdout.printf(" %c", bug.feeler_kind);
}
stdout.printf("\n");
}
}
void print_head() {
stdout.printf(" HHHHHHH\n");
stdout.printf(" H H\n");
stdout.printf(" H O O H\n");
stdout.printf(" H H\n");
stdout.printf(" H V H\n");
stdout.printf(" HHHHHHH\n");
}
void print_neck() {
for (int i = 0; i < neck_length; i++) {
stdout.printf(" N N\n");
}
}
void print_body(bug_type bug) {
stdout.printf(" BBBBBBBBBBBB\n");
for (int i = 0; i < body_height; i++) {
stdout.printf(" B B\n");
}
if (bug.tail == true) {
stdout.printf("TTTTTB B\n");
}
stdout.printf(" BBBBBBBBBBBB\n");
}
void print_legs(bug_type bug) {
for (int i = 0; i < leg_length; i++) {
stdout.printf(" ");
for (int j = 0; j < bug.legs; j++) {
stdout.printf(" L");
}
stdout.printf("\n");
}
}
void print_bug(bug_type bug) {
if (bug.feelers > 0) {
print_feelers(bug);
}
if (bug.head == true) {
print_head();
}
if (bug.neck == true) {
print_neck();
}
if (bug.body == true) {
print_body(bug);
}
if (bug.legs > 0) {
print_legs(bug);
}
}
bool finished(bug_type bug) {
return bug.feelers == max_feelers && bug.tail && bug.legs == max_legs;
}
int dice() {
return GLib.Random.int_range(1, 7);
}
const string[] as_text = {
"no",
"a",
"two",
"three",
"four",
"five",
"six" }; // max_legs
string plural(int number, string noun) {
return as_text[number] + " " + noun + ((number > 1) ? "s" : "");
}
bool add_part(part_id part, ref player_type player) {
bool changed = false;
switch (part) {
case part_id.body:
if (player.bug.body) {
stdout.printf(", but " + player.pronoun + " already have a body.\n");
} else {
stdout.printf("; " + player.pronoun + " now have a body:\n");
player.bug.body = true;
changed = true;
}
break;
case part_id.neck:
if (player.bug.neck) {
stdout.printf(", but " + player.pronoun + " you already have a neck.\n");
} else if (!player.bug.body) {
stdout.printf(", but " + player.pronoun + " need a body first.\n");
} else {
stdout.printf("; " + player.pronoun + " now have a neck:\n");
player.bug.neck = true;
changed = true;
}
break;
case part_id.head:
if (player.bug.head) {
stdout.printf(", but " + player.pronoun + " already have a head.\n");
} else if (!player.bug.neck) {
stdout.printf(", but " + player.pronoun + " need a a neck first.\n");
} else {
stdout.printf("; " + player.pronoun + " now have a head:\n");
player.bug.head = true;
changed = true;
}
break;
case part_id.feeler:
if (player.bug.feelers == max_feelers) {
stdout.printf(", but " + player.pronoun + " have two feelers already.\n");
} else if (!player.bug.head) {
stdout.printf(", but " + player.pronoun + " need a head first.\n");
} else {
player.bug.feelers++;
stdout.printf("; " + player.pronoun + " now have " +
plural(player.bug.feelers, "feeler") + ":\n");
changed = true;
}
break;
case part_id.tail:
if (player.bug.tail) {
stdout.printf(", but " + player.pronoun + " already have a tail.\n");
} else if (!player.bug.body) {
stdout.printf(", but " + player.pronoun + " need a body first.\n");
} else {
stdout.printf("; " + player.pronoun + " now have a tail:\n");
player.bug.tail = true;
changed = true;
}
break;
case part_id.leg:
if (player.bug.legs == max_legs) {
stdout.printf(", but " + player.pronoun + " have " +
as_text[max_legs], " feet already.\n");
} else if (!player.bug.body) {
stdout.printf(", but " + player.pronoun + " need a body first.\n");
} else {
player.bug.legs++;
stdout.printf("; " + player.pronoun + " now have " +
plural(player.bug.legs, "leg") + ":\n");
changed = true;
}
break;
}
return changed;
}
void prompt() {
stdout.printf("Press Enter to roll the dice. ");
stdin.read_line();
erase_previous_line();
}
void turn(ref player_type player) {
prompt();
int number = dice();
part_id part = (part_id)number;
stdout.printf("%s rolled a %i (%s)", capitalized(player.pronoun), number, part_name(part));
if (add_part(part, ref player)) {
stdout.printf("\n");
print_bug(player.bug);
player.bug.finished = finished(player.bug);
}
stdout.printf("\n");
}
void print_winner() {
if (human.bug.finished && computer.bug.finished) {
stdout.printf("Both of our bugs are finished in the same number of turns!\n");
} else if (finished(human.bug)) {
stdout.printf(human.possessive + " bug is finished.\n");
} else if (finished(computer.bug)) {
stdout.printf(computer.possessive + " bug is finished.\n");
}
}
bool game_over() {
return human.bug.finished || computer.bug.finished;
}
void play() {
clear_screen();
do {
turn(ref human);
turn(ref computer);
} while (!game_over());
print_winner();
}
void init() {
human.pronoun = "you";
human.possessive = "Your";
human.bug.feeler_kind = 'A';
human.bug.finished = false;
computer.pronoun = "I";
computer.possessive = "My";
computer.bug.feeler_kind = 'F';
computer.bug.finished = false;
}
void main() {
init();
print_credits();
print_instructions();
play();
stdout.printf("I hope you enjoyed the game, play it again soon!!\n");
}
Bunny
/*
Bunny
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), 1978.
This version in Vala:
Copyright (c) 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written in 2023-08-31.
Last modified 20260828T1048+0200.
*/
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void reset_screen_attributes() {
stdout.printf("\x1B[0m");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_screen_attributes();
move_cursor_home();
}
void print_credits() {
stdout.printf("Bunny\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Creative Computing (Morristown, New Jersey, USA), 1978.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2023, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
stdout.printf("Press Enter to start the program.\n");
stdin.read_line();
}
const int width = 53;
unichar line_buffer[width];
int first_column;
void clear_line_buffer() {
for (int column = 0; column < width; column++) {
line_buffer[column] = ' ';
}
first_column = 0;
}
void print_line_buffer() {
for (int column = 0; column < width; column++) {
stdout.printf(line_buffer[column].to_string());
}
stdout.printf("\n");
}
const string word = "BUNNY";
const int EOL = -1; // end of line identifier
const 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 };
void draw() {
int last_column;
uint data_index = 0;
clear_line_buffer();
while (data_index < data.length) {
first_column = data[data_index];
data_index += 1;
if (first_column == EOL) {
print_line_buffer();
clear_line_buffer();
} else {
last_column = data[data_index];
data_index += 1;
for (int column = first_column; column <= last_column; column++) {
line_buffer[column] = word[column % word.length];
}
}
}
}
void main() {
clear_screen();
print_credits();
clear_screen();
draw();
}
Chase
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2026-08-28/30.
// Last modified: 20260830T0041+0200.
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_cursor_position(int line, int col) {
stdout.printf("\x1B[%d;%dH", line, col);
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_line_to_end() {
stdout.printf("\x1B[K");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
const int DEFAULT_INK = WHITE + FOREGROUND;
const int INPUT_INK = BRIGHT + GREEN + FOREGROUND;
const int INSTRUCTIONS_INK = YELLOW + FOREGROUND;
const int TITLE_INK = BRIGHT + RED + FOREGROUND;
// Data {{{1
// =============================================================
const int ARENA_WIDTH = 20;
const int ARENA_HEIGHT = 10;
const int ARENA_LAST_X = ARENA_WIDTH - 1;
const int ARENA_LAST_Y = ARENA_HEIGHT - 1;
const int ARENA_ROW = 3;
char[,] arena;
const char EMPTY = ' ';
const char FENCE = 'X';
const char MACHINE = 'm';
const char HUMAN = '@';
const int FENCES = 15; // inner obstacles, not the border
enum End {
NOT_YET,
QUIT,
ELECTRIFIED,
KILLED,
VICTORY
}
End the_end = End.NOT_YET;
const int MACHINES = 5;
const int MACHINES_DRAG = 2; // probability not moving: 0=0%, 1=50%, 2=66%, 3=75%, etc.
struct Machine {
int x;
int y;
bool operative;
}
Machine[] machine;
int destroyed_machines = 0; // counter
int human_x = 0;
int human_y = 0;
// User input {{{1
// =============================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
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 (s.down()) {
case "ok":
case "y":
case "yeah":
case "yes":
return true;
default:
return false;
}
}
bool is_no(string s) {
switch (s.down()) {
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
// =============================================================
const string TITLE = "Chase";
void print_title() {
set_style(TITLE_INK);
stdout.printf("%s\n", TITLE);
set_style(DEFAULT_INK);
}
void print_credits() {
print_title();
stdout.printf("\nOriginal version in BASIC:\n");
stdout.printf(" Anonymous.\n");
stdout.printf(" Published in \"The Best of Creative Computing\", Volume 2, 1977.\n");
stdout.printf(" https://www.atariarchives.org/bcc2/showpage.php?page=253\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n");
}
void print_instructions() {
print_title();
set_style(INSTRUCTIONS_INK);
stdout.printf("\nYou (%c) are in a high voltage maze with %d\n", HUMAN, MACHINES);
stdout.printf("security machines (%c) trying to kill you.\n", MACHINE);
stdout.printf("You must maneuver them into the maze (%c) to survive.\n\n", FENCE);
stdout.printf("Good luck!\n\n");
stdout.printf("The movement commands are the following:\n\n");
stdout.printf(" ↖ ↑ ↗\n");
stdout.printf(" NW N NE\n");
stdout.printf(" ← W E →\n");
stdout.printf(" SW S SE\n");
stdout.printf(" ↙ ↓ ↘\n");
stdout.printf("\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++) {
for (int x = 0; x <= ARENA_LAST_X; x++) {
stdout.printf("%c", arena[y, x]);
}
stdout.printf("\n");
}
}
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 GLib.Random.int_range(min, min + max);
}
// Place the given string at a random empty position of the arena and update the
// coordinates.
void place(char s, ref int y, ref int x) {
do {
y = random_int_in_inclusive_range(1, ARENA_LAST_Y - 1);
x = random_int_in_inclusive_range(1, ARENA_LAST_X - 1);
} while (arena[y, x] != EMPTY);
arena[y, x] = s;
}
void place_fences() {
int y = 0;
int x = 0;
for (int i = 0; i < FENCES; i++) {
place(FENCE, ref y, ref x);
}
}
void place_machines() {
for (int m = 0; m < MACHINES; m++) {
place(MACHINE, ref machine[m].y, ref machine[m].x);
machine[m].operative = true;
}
}
void inhabit_arena() {
place_machines();
place_fences();
place(HUMAN, ref human_y, ref human_x);
}
void clean_arena() {
for (int y = 0; y <= ARENA_LAST_Y; y++) {
for (int x = 0; x <= ARENA_LAST_X; x++) {
arena[y, x] = is_border(y, x) ? FENCE : EMPTY;
}
}
}
// Game {{{1
// =============================================================
void init_once() {
arena = new char[ARENA_HEIGHT, ARENA_WIDTH];
machine = new Machine[MACHINES];
}
void init_game() {
clean_arena();
inhabit_arena();
destroyed_machines = 0;
the_end = End.NOT_YET;
}
void move_machine(int m) {
int maybe = 0;
arena[machine[m].y, machine[m].x] = EMPTY;
maybe = GLib.Random.int_range(0, 2);
if (machine[m].y > human_y) {
machine[m].y -= maybe;
}
else if (machine[m].y < human_y) {
machine[m].y += maybe;
}
maybe = GLib.Random.int_range(0, 2);
if (machine[m].x > human_x) {
machine[m].x -= maybe;
}
else if (machine[m].x < human_x) {
machine[m].x += maybe;
}
if (arena[machine[m].y, machine[m].x] == EMPTY) {
arena[machine[m].y, machine[m].x] = MACHINE;
}
else if (arena[machine[m].y, machine[m].x] == FENCE) {
machine[m].operative = false;
destroyed_machines += 1;
if (destroyed_machines == MACHINES) {
the_end = End.VICTORY;
}
}
else if (arena[machine[m].y, machine[m].x] == HUMAN) {
the_end = End.KILLED;
}
}
void maybe_move_machine(int m) {
if (GLib.Random.int_range(0, MACHINES_DRAG) == 0) {
move_machine(m);
}
}
void move_machines() {
for (int m = 0; m < MACHINES; m++) {
if (machine[m].operative) {
maybe_move_machine(m);
}
}
}
// Read a user command; update `the_end` accordingly and set the direction
// increments.
void set_move(ref int y_inc, ref int x_inc) {
stdout.printf("\n");
erase_line_to_end();
string command = get_string("Command: ").down();
switch (command) {
case "q":
the_end = End.QUIT;
break;
case "sw":
y_inc = 1;
x_inc = -1;
break;
case "s":
y_inc = 1;
x_inc = 0;
break;
case "se":
y_inc = 1;
x_inc = 1;
break;
case "w":
y_inc = 0;
x_inc = -1;
break;
case "e":
y_inc = 0;
x_inc = 1;
break;
case "nw":
y_inc = -1;
x_inc = -1;
break;
case "n":
y_inc = -1;
x_inc = 0;
break;
case "ne":
y_inc = -1;
x_inc = 1;
break;
default:
y_inc = 0;
x_inc = 0;
break;
}
}
void play() {
init_once();
int y_inc = 0;
int x_inc = 0;
do {
clear_screen();
print_title();
init_game();
while (the_end == End.NOT_YET) {
print_arena();
set_move(ref y_inc, ref x_inc);
if (the_end == 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 = End.ELECTRIFIED;
}
else if (arena[human_y + y_inc, human_x + x_inc] == MACHINE) {
the_end = 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();
}
}
}
switch (the_end) {
case End.NOT_YET:
break;
case End.QUIT:
stdout.printf("\nSorry to see you quit.\n");
break;
case End.ELECTRIFIED:
stdout.printf("\nZap! You touched the fence!\n");
break;
case End.KILLED:
stdout.printf("\nYou have been killed by a lucky machine.\n");
break;
case End.VICTORY:
stdout.printf("\nYou are lucky, you destroyed all machines.\n");
break;
}
} // action loop;
} while (yes("\nDo you want to play again? "));
stdout.printf("\nHope you don't feel fenced in.\n");
stdout.printf("Try again sometime.\n");
}
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
/*
Diamond
Original version in BASIC:
Example included in Vintage BASIC 1.0.3.
http://www.vintage-basic.net
This version in Vala:
Copyright (c) 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written in 2023-08-30/31.
Last modified 20260828T1048+0200.
*/
void main() {
const int lines = 17;
int i = 1;
int j;
while (i <= lines / 2 + 1) {
j = 1;
while (j <= (lines + 1) / 2 - i + 1) {
stdout.printf(" ");
j += 1;
}
j = 1;
while (j <= i * 2 - 1) {
stdout.printf("*");
j += 1;
}
stdout.printf("\n");
i += 1;
}
i = 1;
while (i <= lines / 2) {
j = 1;
while (j <= i + 1) {
stdout.printf(" ");
j += 1;
}
j = 1;
while (j <= ((lines + 1) / 2 - i) * 2 - 1) {
stdout.printf("*");
j += 1;
}
stdout.printf("\n");
i += 1;
}
}
Hammurabi
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-30.
//
// Last modified: 20260831T0147+0200.
//
// Acknowledgment:
// The following Python port was used as a reference of the original
// variables: <https://github.com/jquast/hamurabi.py>.
using Gsl;
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Data {{{1
// =============================================================
const int ACRES_A_BUSHEL_CAN_SEED = 2; // yearly
const int ACRES_A_PERSON_CAN_SEED = 10; // yearly
const int ACRES_PER_PERSON = 10; // to calculate the initial acres of the city
const int BUSHELS_TO_FEED_A_PERSON = 20; // yearly
const int IRRITATION_LEVELS = 5; // after the switch in `show_irritation`
const int IRRITATION_STEP = MAX_IRRITATION / IRRITATION_LEVELS;
const int MAX_HARVESTED_BUSHELS_PER_ACRE = MIN_HARVESTED_BUSHELS_PER_ACRE + RANGE_OF_HARVESTED_BUSHELS_PER_ACRE - 1;
const int MIN_HARVESTED_BUSHELS_PER_ACRE = 17;
const int MAX_IRRITATION = 16;
const double PLAGUE_CHANCE = 0.15; // 15% yearly
const int RANGE_OF_HARVESTED_BUSHELS_PER_ACRE = 10;
const int YEARS = 10; // goverment period
const int DEFAULT_INK = FOREGROUND + WHITE;
const int INPUT_INK = FOREGROUND + BRIGHT + GREEN;
const int INSTRUCTIONS_INK = FOREGROUND + YELLOW;
const int RESULT_INK = FOREGROUND + BRIGHT + CYAN;
const int SPEECH_INK = FOREGROUND + BRIGHT + MAGENTA;
const int TITLE_INK = FOREGROUND + BRIGHT + WHITE;
const int 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
// =============================================================
const string CREDITS =
"""Hammurabi
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.
This improved remake in Vala:
Copyright (c) 2026, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair""";
void print_credits() {
set_style(TITLE_INK);
stdout.printf("%s\t", CREDITS);
set_style(DEFAULT_INK);
}
const string INSTRUCTIONS =
"""Hammurabi is a simulation game in which you, as the ruler of the ancient
kingdom of Sumeria, Hammurabi, manage the resources.
You may buy and sell land with your neighboring city-states for bushels of
grain ― the price will vary between %d and %d bushels per acre. You also must
use grain to feed your people and as seed to plant the next year's crop.
You will quickly find that a certain number of people can only tend a certain
amount of land and that people starve if they are not fed enough. You also
have the unexpected to contend with such as a plague, rats destroying stored
grain, and variable harvests.
You will also find that managing just the few resources in this game is not a
trivial job. The crisis of population density rears its head very rapidly.
Try your hand at governing ancient Sumeria for a %d-year term of office.""";
void print_instructions() {
set_style(INSTRUCTIONS_INK);
stdout.printf(
INSTRUCTIONS,
MIN_HARVESTED_BUSHELS_PER_ACRE,
MAX_HARVESTED_BUSHELS_PER_ACRE,
YEARS
);
set_style(DEFAULT_INK);
}
// User input {{{1
// =============================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
void pause(string prompt = "> ") {
set_style(INPUT_INK);
accept_string(prompt);
set_style(DEFAULT_INK);
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
// Random numbers {{{1
// =============================================================
int random_1_to_5() {
return GLib.Random.int_range(1, 6);
}
Gsl.RNGType* T;
Gsl.RNG rng;
void init_rng() {
Gsl.RNG.env_setup ();
T = Gsl.RNGTypes.@default;
rng = new Gsl.RNG (T);
}
// 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 singular = "person", string plural = "people") {
switch (n) {
case 0: return "nobody";
case 1: return "one " + singular;
default: return n.to_string() + " " + 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 "your " + year.to_string() + ordinal_suffix(year) + " year";
}
}
void print_annual_report(int year) {
clear_screen();
set_style(SPEECH_INK);
stdout.printf("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);
stdout.printf(
"\nIn %s, %s starved and %s %s born.\n",
year_text,
persons_text,
infants_text,
(infants > 1) ? "were" : "was"
);
population += infants;
if (year > 0 && rng.uniform() <= PLAGUE_CHANCE) {
population = (int)(population / 2);
set_style(WARNING_INK);
stdout.printf("A horrible plague struck! Half the people died.\n");
set_style(DEFAULT_INK);
}
stdout.printf("The population is %d.\n", population);
stdout.printf("The city owns %d acres.\n", acres);
stdout.printf(
"You harvested %d bushels (%d per acre).\n",
bushels_harvested,
bushels_harvested_per_acre
);
if (bushels_eaten_by_rats > 0) {
stdout.printf("The rats ate %d bushels.\n", bushels_eaten_by_rats);
}
stdout.printf("You have %d bushels in store.\n", bushels_in_store);
bushels_harvested_per_acre =
(int)((double)RANGE_OF_HARVESTED_BUSHELS_PER_ACRE * rng.uniform()) +
MIN_HARVESTED_BUSHELS_PER_ACRE;
stdout.printf("Land is trading at %d bushels per acre.\n\n", bushels_harvested_per_acre);
}
void say_bye() {
set_style(DEFAULT_INK);
stdout.printf("\nSo long for now.\n");
}
void quit_game() {
// XXX TODO replace with catched exception in `main`?
say_bye();
GLib.Process.exit(0);
}
void relinquish() {
set_style(SPEECH_INK);
stdout.printf("\nHammurabi, I am deeply irritated and cannot serve you anymore.\n");
stdout.printf("Please, get yourself another steward!\n");
set_style(DEFAULT_INK);
quit_game();
}
void increase_irritation() {
irritation += GLib.Random.int_range(1, IRRITATION_STEP + 1);
if (irritation >= MAX_IRRITATION) {
relinquish(); // this never returns
}
}
void print_irritated(string adverb) {
stdout.printf("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);
stdout.printf("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);
stdout.printf("I beg your pardon? You have only %d %s. Now then…\n", n, name);
set_style(DEFAULT_INK);
show_irritation();
}
void trade_land() {
int acres_to_buy = 0;
int acres_to_sell = 0;
while (true) {
acres_to_buy = accept_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) {
stdout.printf("You buy %d acres.\n", acres_to_buy);
acres += acres_to_buy;
bushels_in_store -= bushels_harvested_per_acre * acres_to_buy;
stdout.printf("You now have %d acres and %d bushels.\n", acres, bushels_in_store);
}
else {
while (true) {
acres_to_sell = accept_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) {
stdout.printf("You sell %d acres.\n", acres_to_sell);
acres -= acres_to_sell;
bushels_in_store += bushels_harvested_per_acre * acres_to_sell;
stdout.printf("You now have %d acres and %d bushels.\n", acres, bushels_in_store);
}
}
}
void feed_people() {
while (true) {
bushels_to_feed_with = accept_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");
}
}
}
stdout.printf("You feed your people with %d bushels.\n", bushels_to_feed_with);
bushels_in_store -= bushels_to_feed_with;
stdout.printf("You now have %d bushels.\n", bushels_in_store);
}
void seed_land() {
int acres_to_seed = 0;
while (true) {
acres_to_seed = accept_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 =
"bushels of grain,\nand one bushel can seed "
+ ACRES_A_BUSHEL_CAN_SEED.to_string()
+ " acres";
// 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 =
"people to tend the fields,\nand one person can seed "
+ ACRES_A_PERSON_CAN_SEED.to_string()
+ " acres";
beg_think_again(population, message);
}
int bushels_used_for_seeding = (acres_to_seed / ACRES_A_BUSHEL_CAN_SEED);
stdout.printf("You seed %d acres using %d bushels.\n", acres_to_seed, bushels_used_for_seeding);
bushels_in_store -= bushels_used_for_seeding;
stdout.printf("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;
}
void init_first_year() {
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 init() {
init_rng();
init_first_year();
}
void print_result(Result r) {
set_style(RESULT_INK);
switch (r) {
case Result.VERY_GOOD:
stdout.printf("A fantastic performance! Charlemagne, Disraeli and Jefferson combined could\n");
stdout.printf("not have done better!\n");
break;
case Result.NOT_TOO_BAD:
stdout.printf("Your performance could have been somewat better, but really wasn't too bad at\n");
stdout.printf(
"all. %d people would dearly like to see you assassinated, but we all have our\n",
(int)(population * 0.8 * rng.uniform())
);
stdout.printf("trivial problems.\n");
break;
case Result.BAD:
stdout.printf("Your heavy-handed performance smacks of Nero and Ivan IV. The people\n");
stdout.printf("(remaining) find you an unpleasant ruler and, frankly, hate your guts!\n");
break;
case Result.VERY_BAD:
stdout.printf("Due to this extreme mismanagement you have not only been impeached and thrown\n");
stdout.printf("out of office but you have also been declared national fink!!!\n");
break;
}
set_style(DEFAULT_INK);
}
void print_final_report() {
clear_screen();
if (starved_people_percentage > 0) {
stdout.printf(
"In your %d-year term of office, %d percent of the\n",
YEARS,
starved_people_percentage
);
stdout.printf(
"population starved per year on the average, i.e., a total of %d people died!\n",
total_dead
);
}
int acres_per_person = acres / population;
stdout.printf(
"You started with %d acres per person and ended with %d.\n",
ACRES_PER_PERSON,
acres_per_person
);
if (starved_people_percentage > 33 || acres_per_person < 7) {
print_result(Result.VERY_BAD);
}
else if (starved_people_percentage > 10 || acres_per_person < 9) {
print_result(Result.BAD);
}
else if (starved_people_percentage > 3 || acres_per_person < 10) {
print_result(Result.NOT_TOO_BAD);
}
else {
print_result(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);
stdout.printf("\nYou starved %d people in one year!!!\n\n", dead);
set_style(DEFAULT_INK);
print_result(Result.VERY_BAD);
quit_game();
}
}
}
void govern() {
init();
print_annual_report(0);
for (int year = 1; year < YEARS + 1; year++) {
trade_land();
feed_people();
seed_land();
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("\n\nPress the Enter key to read the instructions. ");
clear_screen();
print_instructions();
pause("\n\nPress the Enter key to start. ");
govern();
pause("\nPress the Enter key to read the final report. ");
print_final_report();
say_bye();
}
High Noon
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-27.
//
// Last modified: 20260828T1048+0200.
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Global variables and constants {{{1
// =============================================================
const int DEFAULT_INK = FOREGROUND + WHITE;
const int INPUT_INK = FOREGROUND + BRIGHT + GREEN;
const int INSTRUCTIONS_INK = FOREGROUND + YELLOW;
const int TITLE_INK = FOREGROUND + BRIGHT + RED;
const int INITIAL_DISTANCE = 100;
const int INITIAL_BULLETS = 4;
const int 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) {
stdout.printf(prompt);
set_style(INPUT_INK);
string s = stdin.read_line().strip();
set_style(DEFAULT_INK);
return s;
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
bool is_yes(string s) {
switch (s.down()) {
case "ok", "y", "yeah", "yes":
return true;
default:
return false;
}
}
bool is_no(string s) {
switch (s.down()) {
case "n", "no", "nope":
return true;
default:
return false;
}
}
bool yes(string prompt) {
while (true) {
string answer = accept_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);
stdout.printf("High Noon\n");
set_style(DEFAULT_INK);
}
void print_credits() {
print_title();
stdout.printf("\nOriginal version in BASIC:\n");
stdout.printf(" Designed and programmend by Chris Gaylo, 1970.\n");
stdout.printf(" http://mybitbox.com/highnoon-1970/\n");
stdout.printf(" http://mybitbox.com/highnoon/\n");
stdout.printf("Transcriptions:\n");
stdout.printf(" https://github.com/Mr_methor/Highnoon-BASIC/\n");
stdout.printf(" https://github.com/mad4j/basic-highnoon/\n");
stdout.printf("Version modified for QB64:\n");
stdout.printf(" By Daniele Olmisani, 2014.\n");
stdout.printf(" https://github.com/mad4j/basic-highnoon/\n");
stdout.printf("This improved remake in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n");
}
void print_instructions() {
print_title();
set_style(INSTRUCTIONS_INK);
stdout.printf("\nYou have been challenged to a showdown by Black Bart, one of\n");
stdout.printf("the meanest desperadoes west of the Allegheny mountains.\n");
stdout.printf("\nWhile you are walking down a dusty, deserted side street,\n");
stdout.printf("Black Bart emerges from a saloon one hundred paces away.\n");
stdout.printf("\nBy agreement, you each have %d bullets in your six-guns.", INITIAL_BULLETS);
stdout.printf("\nYour marksmanship equals his. At the start of the walk nei-\n");
stdout.printf("ther of you can possibly hit the other, and at the end of\n");
stdout.printf("the walk, neither can miss. the closer you get, the better\n");
stdout.printf("your chances of hitting black Bart, but he also has beter\n");
stdout.printf("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) {
stdout.printf("Both of you have %d bullets.\n", player_bullets);
}
else {
stdout.printf(
"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));
}
}
int random_max(int max) {
return GLib.Random.int_range(0, max + 1);
}
void print_check() {
stdout.printf("******************************************************\n");
stdout.printf("* *\n");
stdout.printf("* BANK OF DODGE CITY *\n");
stdout.printf("* CASHIER'S RECEIT *\n");
stdout.printf("* *\n");
stdout.printf("* CHECK NO. %04d AUGUST %dTH, 1889 *\n",
random_max(999),
10 + random_max(9));
stdout.printf("* *\n");
stdout.printf("* *\n");
stdout.printf("* PAY TO THE BEARER ON DEMAND THE SUM OF *\n");
stdout.printf("* *\n");
stdout.printf("* TWENTY THOUSAND DOLLARS-------------------$20,000 *\n");
stdout.printf("* *\n");
stdout.printf("******************************************************\n");
}
void get_reward() {
stdout.printf("As mayor of Dodge City, and on behalf of its citizens,\n");
stdout.printf("I extend to you our thanks, and present you with this\n");
stdout.printf("reward, a check for $20,000, for killing Black Bart.\n\n\n");
print_check();
stdout.printf("\n\nDon't spend it all in one place.\n");
}
void move_the_opponent() {
int paces = 2 + random_max(7);
stdout.printf("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_max(1) == 0) { // 50% chances
move_the_opponent();
return true;
}
else {
if (!silent) {
stdout.printf("Black Bart stands still.\n");
}
return false;
}
}
bool missed_shot() {
return random_max(9) <= (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) {
stdout.printf("Black Bart fires…\n");
opponent_bullets -= 1;
if (missed_shot()) {
stdout.printf("A miss…\n");
switch (opponent_bullets) {
case 3:
stdout.printf("Whew, were you lucky. That bullet just missed your head.\n");
break;
case 2:
stdout.printf("But Black Bart got you in the right shin.\n");
break;
case 1:
stdout.printf("Though Black Bart got you on the left side of your jaw.\n");
break;
case 0:
stdout.printf("Black Bart must have jerked the trigger.\n");
break;
}
}
else {
if (player_strategy == "j") {
stdout.printf("That trick just saved yout life. Black Bart's bullet\n");
stdout.printf("was stopped by the wood sides of the trough.\n");
}
else {
stdout.printf("Black Bart shot you right through the heart that time.\n");
stdout.printf("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_max(1) == 0) { // 50% chances
stdout.printf("Now is your chance, Black Bart is out of bullets.\n");
}
else {
stdout.printf("Black Bart just hi-tailed it out of town rather than face you\n");
stdout.printf("without a loaded gun. You can rest assured that Black Bart\n");
stdout.printf("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;
bool end_showdown = false;
while (true) {
stdout.printf("You are now %d paces apart from Black Bart.\n", distance);
print_shells_left();
set_style(INSTRUCTIONS_INK);
stdout.printf("\nStrategies:\n");
stdout.printf(" [A]dvance\n");
stdout.printf(" [S]tand still\n");
stdout.printf(" [F]ire\n");
stdout.printf(" [J]ump behind the watering trough\n");
stdout.printf(" [G]ive up\n");
stdout.printf(" [T]urn tail and run\n");
set_style(DEFAULT_INK);
string player_strategy = accept_string("What is your strategy? ").down();
switch (player_strategy) {
case "a": // advance
while (true) {
int paces = accept_integer("How many paces do you advance? ");
if (paces < 0) {
stdout.printf("None of this negative stuff, partner, only positive numbers.\n");
}
else if (paces > 10) {
stdout.printf("Nobody can walk that fast.\n");
}
else {
distance -= paces;
break;
}
}
break;
case "s": // stand still
stdout.printf("That move made you a perfect stationary target.\n");
break;
case "f": // fire
if (player_bullets == 0) {
stdout.printf("You don't have any bullets left.\n");
}
else {
player_bullets -= 1;
if (missed_shot()) {
switch (player_bullets) {
case 2:
stdout.printf("Grazed Black Bart in the right arm.\n");
break;
case 1:
stdout.printf("He's hit in the left shoulder, forcing him to use his right\n");
stdout.printf("hand to shoot with.\n");
break;
default:
break;
}
stdout.printf("What a lousy shot.\n");
if (player_bullets == 0) {
stdout.printf("Nice going, ace, you've run out of bullets.\n");
if (opponent_bullets != 0) {
stdout.printf("Now Black Bart won't shoot until you touch noses.\n");
stdout.printf("You better think of something fast (like run).\n");
}
}
}
else {
stdout.printf("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();
end_showdown = true;
break;
}
}
break;
case "j": // jump
if (watering_troughs == MAX_WATERING_TROUGHS) {
stdout.printf("How many watering troughs do you think are on this street?\n");
player_strategy = "";
}
else {
watering_troughs += 1;
stdout.printf("You jump behind the watering trough.\n");
stdout.printf("Not a bad maneuver to threw Black Bart's strategy off.\n");
}
break;
case "g": // give up
stdout.printf("Black Bart accepts. The conditions are that he won't shoot you\n");
stdout.printf("if you take the first stage out of town and never come back.\n");
if (yes("Agreed? ")) {
stdout.printf("A very wise decision.\n");
end_showdown = true;
break;
}
else {
stdout.printf("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_max(opponent_bullets + 1) == 0) {
stdout.printf("Man, you ran so fast even dogs couldn't catch you.\n");
}
else {
switch (opponent_bullets) {
case 0:
stdout.printf("You were lucky, Black Bart can only throw his gun at you, he\n");
stdout.printf("doesn't have any bullets left. You should really be dead.\n");
break;
case 1:
stdout.printf("Black Bart fires his last bullet…\n");
stdout.printf("He got you right in the back. That's what you deserve, for running.\n");
break;
case 2:
stdout.printf("Black Bart fires and got you twice: in your back\n");
stdout.printf("and your ass. Now you can't even rest in peace.\n");
break;
case 3:
stdout.printf("Black Bart unloads his gun, once in your back\n");
stdout.printf("and twice in your ass. Now you can't even rest in peace.\n");
break;
case 4:
stdout.printf("Black Bart unloads his gun, once in your back\n");
stdout.printf("and three times in your ass. Now you can't even rest in peace.\n");
break;
default:
assert(false);
break;
}
opponent_bullets = 0;
}
end_showdown = true;
break;
default:
stdout.printf("You sure aren't going to live very long if you can't even follow directions.\n");
break;
} // strategy switch
if (end_showdown || the_opponent_kills_or_runs(player_strategy)) {
break;
} else if (player_bullets + opponent_bullets == 0) {
stdout.printf("The showdown must end, because nobody has bullets left.\n");
break;
} else {
stdout.printf("\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
// Math
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-26.
//
// Last modified: 20260828T1048+0200.
using GLib; // Math
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_float(string s) {
bool has_decimal_point = false;
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (s[c] == '.') {
if (has_decimal_point) {
return false;
} else {
has_decimal_point = true;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
float accept_float(string prompt) {
float result;
while (true) {
string s = accept_string(prompt);
if (is_float(s)) {
result = float.parse(s);
break;
} else {
stdout.printf("Real number expected.\n");
}
}
return result;
}
float abs_(float number) {
if (number >= 0) {
return number;
} else {
return number * -1;
}
}
int sign(float number) {
if (number > 0) {
return 1;
} else if (number < 0) {
return -1;
} else {
return 0;
}
}
void main() {
float n = accept_float("Enter a number: ");
stdout.printf(@"ABS($n) -> abs_($n) /* ad hoc function */ -> $(abs_(n))\n");
stdout.printf(@"ATN($n) -> Math.atan($n) -> $(Math.atan(n))\n");
stdout.printf(@"COS($n) -> Math.cos($n) -> $(Math.cos(n))\n");
stdout.printf(@"EXP($n) -> Math.exp($n) -> $(Math.exp(n))\n");
stdout.printf(@"INT($n) -> (int) $n -> $((int) n)\n");
stdout.printf(@"LOG($n) -> Math.log($n) -> $(Math.log(n))\n");
stdout.printf(@"SGN($n) -> sign($n) /* ad hoc function */ -> $(sign(n))\n");
stdout.printf(@"SQR($n) -> Math.sqrt($n) -> $(Math.sqrt(n))\n");
stdout.printf(@"TAN($n) -> Math.tan($n) -> $(Math.tan(n))\n");
}
Mugwump
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-30.
//
// Last modified: 20260901T1122+0200.
using GLib; // Math
// Terminal {{{1
// =============================================================================
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Data {{{1
// =============================================================================
const int GRID_SIZE = 10;
const int TURNS = 10;
const int MUGWUMPS = 4;
struct Mugwump {
int x;
int y;
bool hidden;
}
Mugwump[] mugwump;
int found = 0; // counter
// User input {{{1
// =============================================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
bool is_yes(string s) {
switch (s.down()) {
case "ok":
case "y":
case "yeah":
case "yes":
return true;
default:
return false;
}
}
bool is_no(string s) {
switch (s.down()) {
case "n":
case "no":
case "nope":
return true;
default:
return false;
}
}
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
// =============================================================================
void print_credits() {
clear_screen();
stdout.printf("Mugwump\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).\n");
stdout.printf(" Slightly modified by Bob Albrecht of People's Computer Company.\n");
stdout.printf(" Published by Creative Computing (Morristown, New Jersey, USA), 1978.\n");
stdout.printf(" - https://www.atariarchives.org/basicgames/showpage.php?page=114\n");
stdout.printf(" - http://vintage-basic.net/games.html\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
accept_string("Press Enter to read the instructions. ");
}
void print_instructions() {
clear_screen();
stdout.printf("Mugwump\n\n");
stdout.printf("The object of this game is to find four mugwumps\n");
stdout.printf("hidden on a 10 by 10 grid. Homebase is position 0,0.\n");
stdout.printf("Any guess you make must be two numbers with each\n");
stdout.printf("number between 0 and 9, inclusive. First number\n");
stdout.printf("is distance to right of homebase and second number\n");
stdout.printf("is distance above homebase.\n\n");
stdout.printf("You get %d tries. After each try, you will see\n", TURNS);
stdout.printf("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++) {
mugwump[m].x = GLib.Random.int_range(0, GRID_SIZE);
mugwump[m].y = GLib.Random.int_range(0, GRID_SIZE);
mugwump[m].hidden = true;
}
found = 0; // counter
}
int accept_coordinate(string prompt) {
int coord = 0;
while (true) {
coord = accept_integer(prompt);
if (coord < 0 || coord >= GRID_SIZE) {
stdout.printf("Invalid value %d: not in range [0, %d].\n", coord, GRID_SIZE - 1);
}
else {
break;
}
}
return coord;
}
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) Math.sqrt(
Math.pow((mugwump[m].x - x), 2.0) +
Math.pow((mugwump[m].y - y), 2.0));
}
string plural(int n, string plural_suffix = "s", string singular_suffix = "") {
return n > 1 ? plural_suffix : singular_suffix;
}
void play() {
int x = 0;
int y = 0;
int turn = 0; // counter
while (true) { // game
clear_screen();
hide_mugwumps();
bool quit_turns = false;
for (turn = 1; turn <= TURNS; turn += 1) {
stdout.printf("Turn number %d\n\n", turn);
stdout.printf("What is your guess (in range [0, %d])?\n", GRID_SIZE - 1);
x = accept_coordinate("Distance right of homebase (x-axis): ");
y = accept_coordinate("Distance above homebase (y-axis): ");
stdout.printf("\nYour guess is (%d, %d).\n", x, y);
for (int m = 0; m < MUGWUMPS; m++) {
if (is_here(m, x, y)) {
mugwump[m].hidden = false;
found += 1;
stdout.printf("You have found mugwump %d!\n", m);
if (found == MUGWUMPS) {
quit_turns = true;
break;
}
}
}
if (quit_turns) {
break;
} else {
for (int m = 0; m < MUGWUMPS; m++) {
if (mugwump[m].hidden) {
stdout.printf("You are %d units from mugwump %d.\n", distance(m, x, y) , m);
}
}
stdout.printf("\n");
}
} // turns
if (found == MUGWUMPS) {
stdout.printf("\nYou got them all in %d turn%s!\n\n", turn, plural(turn));
stdout.printf("That was fun! let's play again…\n");
stdout.printf("Four more mugwumps are now in hiding.\n");
}
else {
stdout.printf("\nSorry, that's %d tr%s.\n\n", TURNS, plural(TURNS, "ies", "y"));
stdout.printf("Here is where they're hiding:\n");
for (int m = 0; m < MUGWUMPS; m++) {
if (mugwump[m].hidden) {
stdout.printf("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() {
mugwump = new Mugwump[MUGWUMPS];
}
void main() {
print_credits();
print_instructions();
init();
play();
}
Name
// Name
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-26.
//
// Last modified: 20260828T1048+0200.
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
void main() {
string name = accept_string("What is your name? ");
int number = accept_integer("Enter a number: ");
for (int i = 0; i < number; i++) {
stdout.printf("Hello, %s!\n", name);
}
}
Poetry
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-26.
//
// Last modified: 20260828T1048+0200.
using GLib;
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
const int DEFAULT_INK = FOREGROUND + WHITE;
const int INPUT_INK = FOREGROUND + BRIGHT + GREEN;
const int TITLE_INK = FOREGROUND + BRIGHT + RED;
void print_title() {
set_style(TITLE_INK);
stdout.printf("Poetry\n");
set_style(DEFAULT_INK);
}
void print_credits() {
print_title();
stdout.printf("\nOriginal version in BASIC:\n");
stdout.printf(" Unknown author.\n");
stdout.printf(" Published in \"BASIC Computer Games\",\n");
stdout.printf(" Creative Computing (Morristown, New Jersey, USA), 1978.\n\n");
stdout.printf("This improved remake in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n");
}
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_even(int n) {
return n % 2 == 0;
}
int random_max(int max) {
return GLib.Random.int_range(0, max + 1);
}
void wait() {
Thread.usleep(250000); // 250 ms
}
void play() {
int MAX_PHRASES_AND_VERSES = 20;
// counters:
int action = 0;
int phrase = 0;
int phrases_and_verses = 0;
int verse_chunks = 0;
while (true) {
bool skip_the_rest_of_the_loop = false;
bool manage_the_verse_continuation = true;
bool maybe_add_comma = true;
switch (action) {
case 0:
case 1:
switch (phrase) {
case 0:
stdout.printf("MIDNIGHT DREARY");
break;
case 1:
stdout.printf("FIERY EYES");
break;
case 2:
stdout.printf("BIRD OR FIEND");
break;
case 3:
stdout.printf("THING OF EVIL");
break;
case 4:
stdout.printf("PROPHET");
break;
default:
assert(false);
break;
}
break;
case 2:
switch (phrase) {
case 0:
stdout.printf("BEGUILING ME");
verse_chunks = 2;
break;
case 1:
stdout.printf("THRILLED ME");
break;
case 2:
stdout.printf("STILL SITTING…");
maybe_add_comma = false;
break;
case 3:
stdout.printf("NEVER FLITTING");
verse_chunks = 2;
break;
case 4:
stdout.printf("BURNED");
break;
default:
assert(false);
break;
}
break;
case 3:
switch (phrase) {
case 0:
stdout.printf("AND MY SOUL");
break;
case 1:
stdout.printf("DARKNESS THERE");
break;
case 2:
stdout.printf("SHALL BE LIFTED");
break;
case 3:
stdout.printf("QUOTH THE RAVEN");
break;
case 4:
if (verse_chunks != 0) {
stdout.printf("SIGN OF PARTING");
}
break;
default:
assert(false);
break;
}
break;
case 4:
switch (phrase) {
case 0:
stdout.printf("NOTHING MORE");
break;
case 1:
stdout.printf("YET AGAIN");
break;
case 2:
stdout.printf("SLOWLY CREEPING");
break;
case 3:
stdout.printf("…EVERMORE");
break;
case 4:
stdout.printf("NEVERMORE");
break;
default:
assert(false);
break;
}
break;
case 5:
action = 0;
stdout.printf("\n");
if (phrases_and_verses > MAX_PHRASES_AND_VERSES) {
stdout.printf("\n");
verse_chunks = 0;
phrases_and_verses = 0;
action = 2;
skip_the_rest_of_the_loop = true;
}
else {
manage_the_verse_continuation = false;
}
break;
default:
assert(false);
break;
}
if (!skip_the_rest_of_the_loop) {
if (manage_the_verse_continuation) {
wait();
if (maybe_add_comma && !(verse_chunks == 0 || random_max(99) > 19)) {
stdout.printf(",");
verse_chunks = 2;
}
if (random_max(99) > 65) {
stdout.printf("\n");
verse_chunks = 0;
}
else {
stdout.printf(" ");
verse_chunks += 1;
}
}
action += 1;
phrase = random_max(4);
phrases_and_verses += 1;
if (!(verse_chunks > 0 || is_even(action))) {
stdout.printf(" ");
}
}
}
}
void main() {
clear_screen();
print_credits();
accept_string("\nPress the Enter key to start. ");
clear_screen();
play();
}
Russian Roulette
// Russian Roulette
// Original version in BASIC:
// Creative Computing (Morristown, New Jersey, USA), ca. 1980.
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-27.
//
// Last modified: 20260828T1048+0200.
// Terminal {{{1
// =============================================================================
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// User input {{{1
// =============================================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
void press_enter_to_start() {
accept_string("Press Enter to start. ");
}
// Credits and instructions {{{1
// =============================================================================
void print_credits() {
clear_screen();
stdout.printf("Russian Roulette\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
press_enter_to_start();
}
void print_instructions() {
clear_screen();
stdout.printf("Here is a revolver.\n");
stdout.printf("Type 'f' to spin chamber and pull trigger.\n");
stdout.printf("Type 'g' to give up, and play again.\n");
stdout.printf("Type 'q' to quit.\n\n");
}
// Main {{{1
// =============================================================================
void play() {
int times = 0;
while (true) {
bool quit = false;
print_instructions();
times = 0;
while (!quit) {
string command = accept_string("> ");
switch (command) {
case "f": // fire
if (GLib.Random.int_range(0, 100) > 83) {
stdout.printf("Bang! You're dead!\n");
stdout.printf("Condolences will be sent to your relatives.\n");
quit = true;
}
else {
times += 1;
if (times > 10) {
stdout.printf("You win!\n");
stdout.printf("Let someone else blow his brains out.\n");
quit = true;
}
else {
stdout.printf("Click.\n");
}
}
break;
case "g": // give up
stdout.printf("Chicken!\n");
quit = true;
break;
case "q": // quit
return;
default:
continue;
}
}
press_enter_to_start();
}
}
void bye() {
stdout.printf("Bye!\n");
}
void main() {
print_credits();
play();
bye();
}
Seance
// Seance
// Original version in BASIC:
// By Chris Oxlade, 1983.
// https://archive.org/details/seance.qb64
// https://github.com/chaosotter/basic-games
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-27
//
// Last modified: 20260828T1048+0200.
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_cursor_position(int y, int x) {
stdout.printf("\x1B[%d;%dH", y, x);
}
void hide_cursor() {
stdout.printf("\x1B[?25l");
}
void show_cursor() {
stdout.printf("\x1B[?25h");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_line_to_end() {
stdout.printf("\x1B[K");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Config {{{1
// =============================================================================
const string TITLE = "Seance";
const int MAX_SCORE = 50;
const int MAX_MESSAGE_LENGTH = 6;
const int MIN_MESSAGE_LENGTH = 3;
const char BASE_CHARACTER = '@';
const char PLANCHETTE = '*';
const char SPACE = ' ';
const int FIRST_LETTER_NUMBER = 1;
const int LAST_LETTER_NUMBER = 26;
const int BOARD_INK = BRIGHT + CYAN + FOREGROUND;
const int DEFAULT_INK = WHITE + FOREGROUND;
const int INPUT_INK = BRIGHT + GREEN + FOREGROUND;
const int INSTRUCTIONS_INK = YELLOW + FOREGROUND;
const int MISTAKE_EFFECT_INK = BRIGHT + RED + FOREGROUND;
const int PLANCHETTE_INK = YELLOW + FOREGROUND;
const int TITLE_INK = BRIGHT + RED + FOREGROUND;
const int BOARD_X = 29; // screen column
const int BOARD_Y = 5; // screen line
const int BOARD_HEIGHT = 5; // characters displayed on the left and right borders
const int BOARD_WIDTH = 8; // characters displayed on the top and bottom borders
const int BOARD_PAD = 1; // blank characters separating the board from its left and right borders
const int BOARD_ACTUAL_WIDTH = BOARD_WIDTH + 2 * BOARD_PAD; // screen columns
const int BOARD_BOTTOM_Y = BOARD_HEIGHT + 1; // relative to the board
const int INPUT_X = BOARD_X;
const int INPUT_Y = BOARD_Y + BOARD_BOTTOM_Y + 4;
const int MESSAGES_Y = INPUT_Y;
const int MISTAKE_EFFECT_PAUSE = 3000000; // microseconds
// User input {{{1
// =============================================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
void press_enter(string prompt) {
accept_string(prompt);
}
// Credits and instructions {{{1
// =============================================================================
void print_title() {
set_style(TITLE_INK);
stdout.printf("%s\n", TITLE);
set_style(DEFAULT_INK);
}
void print_credits() {
print_title();
stdout.printf("\nOriginal version in BASIC:\n");
stdout.printf(" Written by Chris Oxlade, 1983.\n");
stdout.printf(" https://archive.org/details/seance.qb64\n");
stdout.printf(" https://github.com/chaosotter/basic-games\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n");
}
void print_instructions() {
print_title();
set_style(INSTRUCTIONS_INK);
stdout.printf("\nMessages from the Spirits are coming through, letter by letter. They want you\n");
stdout.printf("to remember the letters and type them into the computer in the correct order.\n");
stdout.printf("If you make mistakes, they will be angry -- very angry...\n");
stdout.printf("\n");
stdout.printf("Watch for stars on your screen -- they show the letters in the Spirits'\n");
stdout.printf("messages.\n");
set_style(DEFAULT_INK);
}
// Game {{{1
// =============================================================================
int random_int_in_inclusive_range(int min, int max) {
return GLib.Random.int_range(min, max + 1);
}
// Return the x coordinate to print the given text centered on the board.
int board_centered_x(string text) {
return (int) (BOARD_X + (BOARD_ACTUAL_WIDTH - text.length) / 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));
stdout.printf("%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);
}
void print_character(int y, int x, int char_code) {
set_cursor_position(y + BOARD_Y, x + BOARD_X);
stdout.printf("%s", ((char) char_code).to_string());
}
void print_board() {
set_style(BOARD_INK);
for (int i = 1; i <= BOARD_WIDTH; i++) {
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++) {
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
}
stdout.printf("\n");
set_style(DEFAULT_INK);
}
void erase_line_from(int line, int column) {
set_cursor_position(line, column);
erase_line_to_end();
}
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);
stdout.printf("%s\n", effect);
set_style(DEFAULT_INK);
Thread.usleep(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) {
const int LETTER_PAUSE = 1000000; // microseconds
int y = 0;
int x = 0;
string letters = "";
hide_cursor();
for (int i = 0; i < length; i++) {
int letter_number = random_int_in_inclusive_range(
FIRST_LETTER_NUMBER,
LAST_LETTER_NUMBER);
letters += ((char) (BASE_CHARACTER + letter_number)).to_string();
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);
Thread.usleep(LETTER_PAUSE);
set_style(DEFAULT_INK);
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 = accept_string("? ").up();
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();
stdout.printf("\n");
}
Sine Wave
/*
Sine Wave
Original version in BASIC:
Creative Computing (Morristown, New Jersey, USA), ca. 1980.
This version in Vala:
Copyright (c) 2023, Marcos Cruz (programandala.net)
SPDX-License-Identifier: Fair
Written in 2023-08, 2023-09.
Last modified 20260828T1048+0200.
*/
using GLib; // Math needed
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void reset_screen_attributes() {
stdout.printf("\x1B[0m");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_screen_attributes();
move_cursor_home();
}
void print_credits() {
stdout.printf("Sine Wave\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Creative computing (Morristown, New Jersey, USA), ca. 1980.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2023, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
stdout.printf("Press Enter to start the program.\n");
stdin.read_line();
}
string word[2];
void get_words() {
string order[2] = {"first", "second"};
for (int i = 0; i < word.length; i++) {
stdout.printf(@"Enter the $(order[i]) word: ");
word[i] = stdin.read_line();
}
}
string repeat_char(char c, int times) {
string s = "";
for (int i = 0; i < times; i++) {
s += c.to_string();
}
return s;
}
void draw() {
bool even = false;
double angle = 0.0;
for (angle = 0.0; angle <= 40.0; angle += 0.25) {
stdout.printf(repeat_char(' ', (int) (26 + 25 * Math.sin(angle))));
stdout.printf("%s\n", word[(int) even]);
even = !even;
}
}
void main() {
clear_screen();
print_credits();
clear_screen();
get_words();
clear_screen();
draw();
}
Slots
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-30.
//
// Last modified: 20260830T2333+0200.
using GLib;
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void hide_cursor() {
stdout.printf("\x1B[?25l");
}
void show_cursor() {
stdout.printf("\x1B[?25h");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Data {{{1
// =============================================================================
const int REELS = 3;
int reel[REELS];
const int IMAGES = 6;
string image[IMAGES];
const int BAR = 0; // position of "BAR" in `image`; forced in `init_once`
int color[IMAGES];
const int MAX_BET = 100;
const int MIN_BET = 1;
// User input {{{1
// =============================================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
// Credits and instructions {{{1
// =============================================================================
void print_credits() {
clear_screen();
stdout.printf("Slots\n");
stdout.printf("A slot machine simulation.\n\n");
stdout.printf("Original version in BASIC:\n");
stdout.printf(" Creative computing (Morristown, New Jersey, USA).\n");
stdout.printf(" Produced by Fred Mirabelle and Bob Harper on 1973-01-29.\n\n");
stdout.printf("This version in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n\n");
accept_string("Press Enter for instructions. ");
}
void print_instructions() {
clear_screen();
stdout.printf("You are in the H&M casino, in front of one of our\n");
stdout.printf("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:
stdout.printf("DOUBLE!\n");
break;
case 5:
stdout.printf("*DOUBLE BAR*\n");
break;
case 10:
stdout.printf("**TOP DOLLAR**\n");
break;
case 100:
stdout.printf("***JACKPOT***\n");
break;
default:
assert_not_reached();
}
stdout.printf("You won!\n");
return (prize + 1) * bet;
}
void show_standings(int usd) {
stdout.printf("Your standings are %d USD.\n", usd);
}
void print_reels() {
move_cursor_home();
for (int r = 0; r < REELS; r++) {
set_style(color[reel[r]]);
stdout.printf("[%s] ", image[reel[r]]);
}
set_style(NORMAL_STYLE);
stdout.printf("\n");
}
void init_reels() {
for (int i = 0; i < REELS; i++) {
reel[i] = GLib.Random.int_range(0, IMAGES);
}
}
void spin_reels() {
const int SECONDS = 2;
DateTime start_time = new GLib.DateTime.now_local();
hide_cursor();
do {
init_reels();
print_reels();
} while
(
start_time.add_seconds(SECONDS)
.compare(new GLib.DateTime.now_local())
== 1
);
show_cursor();
}
int max(int n1, int n2) {
return n1 > n2 ? n1 : n2;
}
void set_prize(ref int equals, ref int bars) {
for (int i = 0; i < IMAGES; i++) {
int count = 0;
for (int r = 0; r < REELS; r++) {
count += reel[r] == i ? 1 : 0;
}
equals = max(equals, count);
}
for (int r = 0; r < REELS; r++) {
bars += reel[r] == BAR ? 1 : 0;
}
}
void play() {
int standings = 0;
int bet = 0;
init_reels();
bool quit = false;
do {
while (true) {
clear_screen();
print_reels();
bet = accept_integer("Your bet (or 0 to quit): ");
if (bet > MAX_BET) {
stdout.printf("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") {
quit = true;
break;
}
} else {
break;
}
}
if (!quit) {
clear_screen();
spin_reels();
int equals = 0;
int bars = 0;
set_prize(ref equals, ref bars);
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:
stdout.printf("You lost.\n");
standings -= bet;
break;
} // prize check
show_standings(standings);
accept_string("Press Enter to continue. ");
}
} while (!quit);
show_standings(standings);
if (standings < 0) {
stdout.printf("Pay up! Please leave your money on the terminal.\n");
}
else if (standings > 0) {
stdout.printf("Collect your winnings from the H&M cashier.\n");
}
else {
stdout.printf("Hey, you broke even.\n");
}
}
void init_once() {
const string BAR_IMAGE = " BAR ";
image = {BAR_IMAGE, " BELL ", "ORANGE", "LEMON ", " PLUM ", "CHERRY"};
assert(image[BAR] == BAR_IMAGE);
color = {
FOREGROUND + WHITE,
FOREGROUND + CYAN,
FOREGROUND + YELLOW,
FOREGROUND + BRIGHT + YELLOW,
FOREGROUND + BRIGHT + WHITE,
FOREGROUND + BRIGHT + RED };
}
void main() {
print_credits();
print_instructions();
init_once();
play();
}
Stars
// Stars
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-26.
//
// Last modified: 20260828T1048+0200.
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
bool is_yes(string s) {
switch (s.down()) {
case "ok", "y", "yeah", "yes":
return true;
default:
return false;
}
}
void print_stars(int number) {
for (int c = 0; c < number; c++) {
stdout.printf("*");
}
stdout.printf("\n");
}
void main() {
string name = accept_string("What is your name? ");
stdout.printf("Hello, %s.\n", name);
do {
print_stars(accept_integer("How many stars do you want? "));
} while (is_yes(accept_string("Do you want more stars? ")));
}
Strings
// Strings
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-26.
//
// Last modified: 20260828T1048+0200.
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
string spc(int number) {
var builder = new StringBuilder();
for (int i = 0; i < number; i++) {
builder.append(" ");
}
return builder.str;
}
void main() {
string s = accept_string("Enter a string: ");
int n = accept_integer("Enter an integer: ");
// XXX TODO limit the bounds of `left$`, `mid$` and `right$`.
// XXX TODO catch the errors of `val` and return 0 instead.
stdout.printf(@"ASC(\"$s\") --> (int) \"$s\"[0] --> $((int) s[0])\n");
stdout.printf(@"CHR$$($n) --> (char) $n --> \"$((char) n)\"\n");
stdout.printf(@"LEFT$$(\"$s\", $n) --> \"$s\"[0 : $n] --> \"$(s[0 : n])\"\n");
stdout.printf(@"LEFT$$(\"$s\", $n) --> \"$s\".substring(0, $n) --> \"$(s.substring(0,n))\"\n");
stdout.printf(@"MID$$(\"$s\", $n) --> \"$s\"[$n :] --> \"$(s[n :])\"\n");
stdout.printf(@"MID$$(\"$s\", $n) --> \"$s\".substring($n) --> \"$(s.substring(n))\"\n");
stdout.printf(@"MID$$(\"$s\", $n, 3) --> \"$s\"[$n : $n + 3] --> \"$(s[n : n + 3])\"\n");
stdout.printf(@"MID$$(\"$s\", $n, 3) --> \"$s\".substring($n, 3) --> \"$(s.substring(n, 3))\"\n");
stdout.printf(@"RIGHT$$(\"$s\", $n) --> \"$s\"[-$n :] --> \"$(s[-n :])\"\n");
stdout.printf(@"RIGHT$$(\"$s\", $n) --> \"$s\".substring(-$n) --> \"$(s.substring(-n))\"\n");
stdout.printf(@"LEN(\"$s\") --> \"$s\".length --> $(s.length)\n");
stdout.printf(@"VAL(\"$s\") --> float.parse(\"$s\") --> $(float.parse(s))\n");
stdout.printf(@"STR$$($n) --> $n.to_string() --> \"$(n.to_string())\"\n");
stdout.printf(@"SPC($n) --> spc($n) /* ad hoc method */ --> \"$(spc(n))\"");
}
Xchange
// 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 Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-30.
//
// Last modified: 20260830T1627+0200.
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_cursor_position(int line, int column) {
stdout.printf("\x1B[%d;%dH", line, column);
}
void set_cursor_coordinate(Coordinate coordinate) {
set_cursor_position(coordinate.y, coordinate.x);
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_line_to_end() {
stdout.printf("\x1B[K");
}
void erase_screen_to_end() {
stdout.printf("\x1B[J");
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Data {{{1
// =============================================================
const int BOARD_INK = FOREGROUND + BRIGHT + CYAN;
const int DEFAULT_INK = FOREGROUND + WHITE;
const int INPUT_INK = FOREGROUND + BRIGHT + GREEN;
const int INSTRUCTIONS_INK = FOREGROUND + YELLOW;
const int TITLE_INK = FOREGROUND + BRIGHT + RED;
const string BLANK = "*";
const int GRID_HEIGHT = 3; // cell rows
const int GRID_WIDTH = 3; // cell columns
const int CELLS = GRID_WIDTH * GRID_HEIGHT;
string[] pristine_grid;
const int GRIDS_Y = 3; // screen row where the grids are printed
const int GRIDS_X = 5; // screen column where the left grid is printed
const int CELLS_GAP = 2; // distance between the grid cells, in screen rows or columns
const int GRIDS_GAP = 16; // screen columns between equivalent cells of the grids
const int FIRST_PLAYER = 0;
const int MAX_PLAYERS = 4;
string[, ] grid;
bool[] is_playing;
int players = 0;
const string QUIT_COMMAND = "X";
// User input {{{1
// =============================================================
string accept_string(string prompt) {
stdout.printf(prompt);
return stdin.read_line().strip();
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
string accept_command(string prompt) {
set_style(INPUT_INK);
string command = accept_string(prompt);
set_style(DEFAULT_INK);
return command;
}
void press_enter(string prompt) {
accept_string(prompt);
}
bool is_yes(string s) {
switch (s.down()) {
case "ok":
case "y":
case "yeah":
case "yes":
return true;
default:
return false;
}
}
bool is_no(string s) {
switch (s.down()) {
case "n":
case "no":
case "nope":
return true;
default:
return false;
}
}
bool yes(string prompt) {
while (true) {
string answer = accept_command(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);
stdout.printf("Xchange\n");
set_style(DEFAULT_INK);
}
void print_credits() {
print_title();
stdout.printf("\nOriginal version in BASIC:\n");
stdout.printf(" Written by Thomas C. McIntire, 1979.\n");
stdout.printf(" Published in \"The A to Z Book of Computer Games\", 1979.\n");
stdout.printf(" https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up\n");
stdout.printf(" https://github.com/chaosotter/basic-games\n");
stdout.printf("This improved remake in Vala:\n");
stdout.printf(" Copyright (c) 2026, Marcos Cruz (programandala.net)\n");
stdout.printf(" SPDX-License-Identifier: Fair\n");
}
void print_instructions() {
print_title();
set_style(INSTRUCTIONS_INK);
stdout.printf("\nOne or two may play. If two, you take turns. A grid looks like this:\n\n");
set_style(BOARD_INK);
stdout.printf(" F G D\n");
stdout.printf(" A H %s\n", BLANK);
stdout.printf(" E B C\n\n");
set_style(INSTRUCTIONS_INK);
stdout.printf("But it should look like this:\n\n");
set_style(BOARD_INK);
stdout.printf(" A B C\n");
stdout.printf(" D E F\n");
stdout.printf(" G H %s\n\n", BLANK);
set_style(INSTRUCTIONS_INK);
stdout.printf("You may exchange any one letter with the '%s', but only one that's adjacent:\n", BLANK);
stdout.printf("above, below, left, or right. Not all puzzles are possible, and you may enter\n");
stdout.printf("'%s' to give up.\n\n", QUIT_COMMAND);
stdout.printf("Here we go...\n");
set_style(DEFAULT_INK);
}
// Grids {{{1
// =============================================================
void print_grid_title(int player) {
set_cursor_position(GRIDS_Y, GRIDS_X + (player * GRIDS_GAP));
stdout.printf("Player %d", player + 1);
}
struct Coordinate {
int y;
int x;
}
Coordinate cell_coordinate(int player, int cell) {
int grid_y = cell / GRID_HEIGHT;
int grid_x = cell % GRID_WIDTH;
int title_margin = players > 1 ? 2 : 0;
int y = GRIDS_Y + title_margin + grid_y;
int x = GRIDS_X + (grid_x * CELLS_GAP) + (player * GRIDS_GAP);
Coordinate result = Coordinate() { y = y, x = x };
return result;
}
Coordinate grid_prompt_coordinate(int player) {
Coordinate result = cell_coordinate(player, CELLS);
result.y += 1;
return result;
}
void print_grid(int player, int color = BOARD_INK) {
if (players > 1) {
print_grid_title(player);
}
set_style(color);
for (int cell = 0; cell < CELLS; cell++) {
set_cursor_coordinate(cell_coordinate(player, cell));
stdout.printf(grid[player, cell]);
}
set_style(DEFAULT_INK);
}
void print_grids() {
for (int player = 0; player < players; player++) {
if (is_playing[player]) {
print_grid(player);
}
}
stdout.printf("\n");
erase_screen_to_end();
}
void scramble_grid(int player) {
for (int cell = 0; cell < CELLS; cell++) {
int random_cell = GLib.Random.int_range(0, CELLS);
// Exchange the contents of the current cell with that of the random one.
string temp = grid[player, cell];
grid[player, cell] = grid[player, random_cell];
grid[player, random_cell] = temp;
}
}
void init_grids() {
for (int i = 0; i < pristine_grid.length; i++) {
grid[0, i] = pristine_grid[i];
}
scramble_grid(0);
for (int player = 1; player < players; player++) {
for (int i = 0; i < pristine_grid.length; i++) {
grid[player, i] = grid[0, i];
}
}
}
// Messages {{{1
// =============================================================
string player_prefix(int player) {
return players > 1 ? @"Player $(player + 1): " : "";
}
Coordinate message_coordinate(int player, int y_inc = 0 ) {
Coordinate result = grid_prompt_coordinate(player);
result.y += 2 + y_inc;
result.x = 1;
return result;
}
void print_message(string message, int player, int y_inc = 0) {
set_cursor_coordinate(message_coordinate(player, y_inc));
stdout.printf("%s%s", player_prefix(player), message);
erase_line_to_end();
stdout.printf("\n");
}
void erase_message(int player) {
set_cursor_coordinate(message_coordinate(player));
erase_line_to_end();
}
// Game loop {{{1
// =============================================================
string players_range_message() {
return MAX_PLAYERS == 2 ? "1 or 2" : @"from 1 to $MAX_PLAYERS";
}
int number_of_players() {
int players = 0;
print_title();
stdout.printf("\n");
if (MAX_PLAYERS == 1) {
players = 1;
}
else {
while (players < 1 || players > MAX_PLAYERS) {
string prompt = @"Number of players ($(players_range_message())): ";
players = accept_integer(prompt);
}
}
return players;
}
bool is_first_cell_of_grid_row(int cell) {
return cell % GRID_WIDTH == 0;
}
bool is_last_cell_of_grid_row(int cell) {
return (cell + 1) % GRID_WIDTH == 0;
}
bool are_cells_adjacent(int cell1, int cell2) {
return (cell2 == cell1 + 1 && !is_first_cell_of_grid_row(cell2)) ||
(cell2 == cell1 + GRID_WIDTH) ||
(cell2 == cell1 - 1 && !is_last_cell_of_grid_row(cell2)) ||
(cell2 == cell1 - GRID_WIDTH);
}
const int INVALID_POSITION = -1;
// 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 -1.
int position_to_cell(int player, int char_cell) {
for (int cell = 0; cell < CELLS; cell++) {
if (grid[player, cell] == BLANK) {
if (are_cells_adjacent(char_cell, cell)) {
return cell;
}
else {
break;
}
}
}
print_message(@"Illegal move \"$(grid[player, char_cell])\".", player);
return INVALID_POSITION;
}
// If the given player's command is valid, i.e. a grid character, return its
// position; otherwise return `INVALID_POSITION`.
int command_to_position(int player, string command) {
if (command != BLANK) {
for (int position = 0; position < CELLS; position++) {
if (command == grid[player, position]) {
return position;
}
}
}
print_message(@"Invalid character \"$command\".", player);
return INVALID_POSITION;
}
void forget_player(int player) {
is_playing[player] = false;
print_grid(player, DEFAULT_INK);
}
void play_turn(int player) {
int blank_position = 0;
int character_position = 0;
if (is_playing[player]) {
while (true) {
while (true) {
Coordinate coordinate = grid_prompt_coordinate(player);
set_cursor_coordinate(coordinate);
erase_line_to_end();
set_cursor_coordinate(coordinate);
string command = accept_command("Move: ").up();
if (command == QUIT_COMMAND) {
forget_player(player);
return;
}
int position = command_to_position(player, command);
if (position != INVALID_POSITION) {
character_position = position;
break;
}
}
int position = position_to_cell(player, character_position);
if (position != INVALID_POSITION) {
blank_position = position;
break;
}
}
erase_message(player);
grid[player, blank_position] = grid[player, character_position];
grid[player, character_position] = BLANK;
}
}
void play_turns() {
for (int player = 0; player < players; player++) {
play_turn(player);
}
}
bool is_someone_playing() {
for (int player = 0; player < players; player++) {
if (is_playing[player]) {
return true;
}
}
return false;
}
bool has_an_empty_grid(int player) {
for (int i = 0; i < CELLS; i++) {
if (grid[player, i] != "") {
return false;
}
}
return true;
}
// If someone has won, 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++) {
if (is_playing[player]) {
if (has_an_empty_grid(player)) {
winners += 1;
if (winners > 0) {
print_message(
"You're the winner" + (winners > 1 ? ", too!" : "!"),
player,
winners - 1);
}
}
}
}
return winners > 0;
}
void init_game() {
clear_screen();
players = number_of_players();
for (int player = 0; player < players; player++) {
is_playing[player] = true;
}
clear_screen();
print_title();
init_grids();
print_grids();
}
void play() {
init_game();
while (is_someone_playing()) {
play_turns();
print_grids();
if (has_someone_won()) {
break;
}
}
}
// Main {{{1
// =============================================================
void init_once() {
is_playing = new bool[MAX_PLAYERS];
grid = new string[MAX_PLAYERS, CELLS];
pristine_grid = new string[CELLS];
const int FIRST_CHAR_CODE = (int) 'A';
for (int cell = 0; cell < CELLS - 1; cell++) {
pristine_grid[cell] = ((char) (FIRST_CHAR_CODE + cell)).to_string();
}
pristine_grid[CELLS - 1] = BLANK;
}
bool enough() {
set_cursor_coordinate(grid_prompt_coordinate(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;
}
}
stdout.printf("So long…\n");
}
Z-End
// Z-End
// Original version in BASIC:
// A to Z Book of Computer Games, by Thomas C. McIntire, 1979.
// - https://archive.org/details/A_to_Z_Book_of_Computer_Games_1979_Thomas_C_McIntire/page/n293/mode/2up
// - https://github.com/chaosotter/basic-games/tree/master/games/A%20to%20Z%20Book%20of%20Computer%20Games/Z-End
// This version in Vala:
// Copyright (c) 2026, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2026-08-27.
//
// Last modified: 20260828T1048+0200.
// Terminal {{{1
// =============================================================================
const int BLACK = 0;
const int RED = 1;
const int GREEN = 2;
const int YELLOW = 3;
const int BLUE = 4;
const int MAGENTA = 5;
const int CYAN = 6;
const int WHITE = 7;
const int DEFAULT = 9;
const int STYLE_OFF = 20;
const int FOREGROUND = 30;
const int BACKGROUND = 40;
const int BRIGHT = 60;
const int NORMAL_STYLE = 0;
const int DEFAULT_COLOR = WHITE;
const int ALPHABET_COLOR = MAGENTA;
const int INPUT_COLOR = GREEN;
const int INSTRUCTIONS_COLOR = YELLOW;
const int TITLE_COLOR = RED;
void move_cursor_home() {
stdout.printf("\x1B[H");
}
void set_style(int style) {
stdout.printf("\x1B[%dm", style);
}
void set_color(int n) {
set_style(n + FOREGROUND);
}
void reset_attributes() {
set_style(NORMAL_STYLE);
}
void erase_screen() {
stdout.printf("\x1B[2J");
}
void clear_screen() {
erase_screen();
reset_attributes();
move_cursor_home();
}
// Input {{{1
// =============================================================================
string accept_string(string prompt) {
string result;
set_color(INPUT_COLOR);
stdout.printf(prompt);
result = stdin.read_line().strip();
set_color(DEFAULT_COLOR);
return result;
}
bool is_sign(char character) {
return character == '+' || character == '-';
}
bool is_digit(char character) {
return character >= '0' && character <= '9';
}
bool is_integer(string s) {
for (int c = 0; c < s.length; c++) {
if (c == 0) {
if (!is_sign(s[c]) && !is_digit(s[c])) {
return false;
}
} else if (!is_digit(s[c])) {
return false;
}
}
return true;
}
int accept_integer(string prompt) {
int result;
while (true) {
string s = accept_string(prompt);
if (is_integer(s)) {
result = int.parse(s);
break;
} else {
stdout.printf("Integer expected.\n");
}
}
return result;
}
bool is_yes(string s) {
switch (s.down()) {
case "ok", "y", "yeah", "yes":
return true;
default:
return false;
}
}
// Main {{{1
// =============================================================================
enum player_id {
computer,
human,
}
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void print_rules() {
clear_screen();
set_color(TITLE_COLOR);
stdout.printf("Z-End\n\n");
string answer = accept_string("Skip the rules? (Y/N) ");
if (!is_yes(answer)) {
set_color(INSTRUCTIONS_COLOR);
stdout.printf("\n");
stdout.printf("I'll print the alphabet, and you're first. You type the number of letters\n");
stdout.printf("that I should omit next time. We take turns, and the limit per turn is five.\n");
stdout.printf("The one that gets the 'Z' is the loser, and that's Z-End!\n");
stdout.printf("\n");
stdout.printf("Good luck, cuz I'm clever...\n");
set_color(DEFAULT_COLOR);
}
stdout.printf("\n");
}
int first_letter;
int computer_pick() {
int picked;
int remaining_letters = (int) (alphabet.length - first_letter);
if (remaining_letters < 6) {
picked = remaining_letters - 1;
}
else if (remaining_letters > 10) {
picked = GLib.Random.int_range(1, 5 + 1);
}
else {
picked = 1;
}
stdout.printf(@"My pick is $picked.\n");
return picked;
}
int human_pick() {
int picked;
while (true) {
picked = accept_integer("Your turn (1-5) ");
set_color(DEFAULT_COLOR);
if (picked < 1 || picked > 5) {
stdout.printf("Illegal entry -- must be in range 1 to 5!\n");
}
else {
break;
}
}
return picked;
}
bool game_over() {
return first_letter == alphabet.length - 1;
}
void print_alphabet(int omitted_letters = 0) {
first_letter += omitted_letters;
set_color(ALPHABET_COLOR);
stdout.printf(alphabet[first_letter :]);
stdout.printf("\n\n");
set_color(DEFAULT_COLOR);
}
void print_result(player_id player) {
stdout.printf("Z-End -- ");
switch (player) {
case player_id.computer:
stdout.printf("Ha ha!\n");
break;
case player_id.human:
stdout.printf("Oops!\n");
break;
}
}
int pick(player_id player) {
switch (player) {
case player_id.computer:
return computer_pick();
case player_id.human:
return human_pick();
}
assert_not_reached();
}
bool playing(player_id player) {
int picked = pick(player);
print_alphabet(picked);
if (game_over()) {
print_result(player);
}
return !game_over();
}
void play() {
first_letter = 0;
print_alphabet();
while (playing(player_id.human) && playing(player_id.computer)) { }
}
bool again() {
stdout.printf("\n");
string answer = accept_string("Do it again (Y/N) ");
return is_yes(answer);
}
void main() {
print_rules();
do {
play();
} while (again());
stdout.printf("\nGoodbye.\n");
}
