Basics of C#
Descripción del contenido de la página
Conversión de antiguos programas de BASIC a C# para aprender los rudimentos de este lenguaje.
Etiquetas:
3D Plot
// 3D Plot
// Original version in BASIC, "3D Plot":
// 3-D Plot (by Mark Bramhall), 1978.
// Creative Computing's BASIC Games.
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/3dplot.bas
// - https://www.atariarchives.org/basicgames/showpage.php?page=167
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-20.
//
// Last modified: 20251205T1528+0100.
using System;
class D3Plot
{
static void PrintCredits()
{
Console.WriteLine("3D Plot\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Creative computing (Morristown, New Jersey, USA), ca. 1980.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
Console.Write("Press any key to start. ");
Console.ReadKey();
}
static double A(double z)
{
return 30 * Math.Exp(-z * z / 100);
}
static void Draw()
{
const int WIDTH = 56;
const char SPACE = ' ';
const char DOT = '*';
int l = 0;
int z = 0;
int y = 0;
int y1 = 0;
char[] line = new char[WIDTH];
double x = -30.0;
while (x <= 30.0)
{
for (int i = 0; i < WIDTH; i++)
{
line[i] = SPACE;
}
l = 0;
y1 = 5 * (int)(Math.Sqrt(900 - x * x) / 5);
y = y1;
while (y >= -y1)
{
z = (int)(25 + A(Math.Sqrt(x * x + (double)(y * y))) - 0.7 * y);
if (z > l)
{
l = z;
line[z] = DOT;
};
y += -5;
} // y loop
foreach (char c in line)
{
Console.Write(c);
}
Console.WriteLine();
x += 1.5;
} // x loop
}
static void Main()
{
Console.Clear();
PrintCredits();
Console.Clear();
Draw();
}
}
Bagels
// Bagels
// Original version in BASIC:
// By D. Resek, P. Rowe, 1978.
// Creative Computing's BASIC Games.
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/bagels.bas
// - http://www.atariarchives.org/basicgames/showpage.php?page=9
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-25.
//
// Last modified: 20251205T1530+0100.
using System;
using System.Linq;
class bagels
{
// Clear the screen, display the credits and wait for a keypress.
//
static void PrintCredits()
{
Console.Clear();
Console.WriteLine("Bagels");
Console.WriteLine("Number guessing game\n");
Console.WriteLine("Original source unknown but suspected to be:");
Console.WriteLine(" Lawrence Hall of Science, U.C. Berkely.\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" D. Resek, P. Rowe, 1978.");
Console.WriteLine(" Creative computing (Morristown, New Jersey, USA), 1978.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
Console.Write("Press Enter to read the instructions. ");
Console.ReadKey();
}
// Clear the screen, print the instructions and wait for a keypress.
//
static void PrintInstructions()
{
Console.Clear();
Console.WriteLine("Bagels");
Console.WriteLine("Number guessing game\n");
Console.WriteLine("I am thinking of a three-digit number that has no two digits the same.");
Console.WriteLine("Try to guess it and I will give you clues as follows:\n");
Console.WriteLine(" PICO - one digit correct but in the wrong position");
Console.WriteLine(" FERMI - one digit correct and in the right position");
Console.WriteLine(" BAGELS - no digits correct");
Console.Write("\nPress Enter to start. ");
Console.ReadKey();
}
const int DIGITS = 3;
static int[] randomDigit = new int[DIGITS];
// Return three random digits.
//
static int[] RandomDigits()
{
Random rand = new Random();
for (int i = 0; i < DIGITS; i++)
{
while (true)
{
digitLoop:
randomDigit[i] = rand.Next(10);
for (int j = 0; j < i; j++)
{
if (i != j && randomDigit[i] == randomDigit[j])
{
goto digitLoop;
}
}
break;
}
}
return randomDigit;
}
// Return `true` if any of the elements of the given array, which contains
// only numbers in inclusive range 0..9, is repeated; otherwise return
// `false`.
//
static bool IsAnyRepeated(int[] num)
{
bool[] present = new bool[10];
foreach (int n in num)
{
if (present[n])
return true;
else
present[n] = true;
}
return false;
}
// Print the given prompt and update the array whose address is given with a
// three-digit number from the user.
//
static void GetInput(string prompt, ref int[] userDigit)
{
// XXX TODO Create a local array, appending every digit after checking
// the contents, making `IsAnyRepeated` unnecessary.
while (true)
{
getLoop:
Console.Write(prompt);
string input = Console.ReadLine();
if (input.Length != DIGITS)
{
Console.WriteLine($"Remember it's a {DIGITS}-digit number.");
continue;
}
char digit;
for (int i = 0; i < input.Length; i++)
{
digit = input[i];
if (Char.IsDigit(digit))
{
userDigit[i] = ((int) digit) - ((int) '0');
}
else
{
Console.WriteLine("What?");
goto getLoop;
}
}
if (IsAnyRepeated(userDigit))
{
Console.WriteLine("Remember my number has no two digits the same.");
continue;
}
break;
}
}
// Return `true` if the given string is "yes" or a synonym.
//
static bool IsYes(string s)
{
string[] validOptions = {"ok", "y", "yeah", "yes"};
return validOptions.Contains(s.ToLower());
}
// Return `true` if the given string is "no" or a synonym.
//
static bool IsNo(string s)
{
string[] validOptions = {"n", "no", "nope"};
return validOptions.Contains(s.ToLower());
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
static bool Yes(string prompt)
{
while (true)
{
Console.Write(prompt);
string answer = Console.ReadLine();
if (IsYes(answer))
{
return true;
}
if (IsNo(answer))
{
return false;
}
}
}
static void Play()
{
const int TRIES = 20;
int score = 0;
int fermi = 0; // counter
int pico = 0; // counter
int[] userNumber = new int[DIGITS];
while (true)
{
Console.Clear();
int[] computerNumber = RandomDigits();
Console.WriteLine("O.K. I have a number in mind.");
for (int guess = 1; guess <= TRIES; guess++)
{
GetInput($"Guess #{guess}: ", ref userNumber);
fermi = 0;
pico = 0;
for (int i = 0; i < DIGITS; i++)
{
for (int j = 0; j < DIGITS; j++)
{
if (userNumber[i] == computerNumber[j])
{
if (i == j)
{
fermi += 1;
}
else
{
pico += 1;
}
}
}
}
for (int i = 0; i < pico; i++)
Console.Write("PICO ");
for (int i = 0; i < fermi; i++)
Console.Write("FERMI ");
if (pico + fermi == 0)
Console.Write("BAGELS");
Console.WriteLine();
if (fermi == DIGITS)
{
break;
}
}
if (fermi == DIGITS)
{
Console.WriteLine("You got it!!!");
score += 1;
}
else
{
Console.WriteLine("Oh well.");
Console.Write($"That's {TRIES} guesses. My number was ");
for (int i = 0; i < DIGITS; i++)
{
Console.Write(computerNumber[i]);
}
Console.WriteLine(".");
}
if (!Yes("Play again? "))
{
break;
}
}
if (score != 0)
{
Console.WriteLine($"A {score}-point bagels, buff!!");
}
Console.WriteLine("Hope you had fun. Bye.");
}
static void Main()
{
PrintCredits();
PrintInstructions();
Play();
}
}
Bug
// Bug
// Original version in BASIC:
// Brian Leibowitz, 1978.
// Creative Computing's BASIC Games.
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/bug.bas
// - https://www.atariarchives.org/basicgames/showpage.php?page=30
// - http://www.retroarchive.org/cpm/games/ccgames.zip
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-20/21.
//
// Last modified: 20251205T1532+0100.
using System;
class BugGame
{
class ABug
{
internal bool body = false;
internal bool neck = false;
internal bool head = false;
internal int feelers = 0;
internal char feelerType = ' ';
internal bool tail = false;
internal int legs = 0;
}
class Player
{
internal string pronoun = "";
internal string possessive = "";
internal ABug bug = new ABug();
}
static Player computer = new Player();
static Player human = new Player();
enum Part {body = 1, neck, head, feeler, tail, leg};
static int parts = Enum.GetNames(typeof(Part)).Length;
// Bug body attributes.
//
const int BODY_HEIGHT = 2;
const int FEELER_LENGTH = 4;
const int LEG_LENGTH = 2;
const int MAX_FEELERS = 2;
const int MAX_LEGS = 6;
const int NECK_LENGTH = 2;
static void PrintCredits()
{
Console.Clear();
Console.WriteLine("Bug\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Brian Leibowitz, 1978.");
Console.WriteLine(" Creative computing (Morristown, New Jersey, USA), 1978.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
Console.Write("Press Enter to read the instructions. ");
Console.ReadKey();
}
// Return the given string with its first character in uppercase and the
// rest in lowercase.
//
static string Capitalized(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
else
{
return char.ToUpper(str[0]) + str.Substring(1).ToLower();
}
}
// Write the given string the given number of times.
//
static void WriteTimes(string s, int n)
{
for (int t = 0; t < n; t++)
{
Console.Write(s);
}
}
// Print a table with the description of the bug parts.
//
static void PrintPartsTable()
{
const int COLUMNS = 3;
const int COLUMN_WIDTH = 8;
const int COLUMN_SEPARATION = 2;
// Headers
string[] header = {"Number", "Part", "Quantity"};
for (int i = 0; i < COLUMNS; i++)
{
Console.Write(header[i].PadRight(COLUMN_WIDTH + COLUMN_SEPARATION));
}
Console.WriteLine();
// Rulers
for (int i = 0; i < COLUMNS; i++)
{
WriteTimes("-", COLUMN_WIDTH);
if (i != COLUMNS - 1)
{
WriteTimes(" ", COLUMN_SEPARATION);
}
}
Console.WriteLine();
// Data
int[] partQuantity = new int[parts + 1];
partQuantity[(int) Part.body] = 1;
partQuantity[(int) Part.neck] = 1;
partQuantity[(int) Part.head] = 1;
partQuantity[(int) Part.feeler] = 2;
partQuantity[(int) Part.tail] = 1;
partQuantity[(int) Part.leg] = 6;
for (Part p = Part.body; (int) p < parts; p++)
{
Console.Write($"{(int) p}".PadRight(COLUMN_WIDTH + COLUMN_SEPARATION));
Console.Write(Capitalized(Enum.GetName(typeof(Part), p).PadRight(COLUMN_WIDTH + COLUMN_SEPARATION)));
Console.WriteLine(partQuantity[(int) p]);
}
}
static void PrintInstructions()
{
Console.Clear();
Console.WriteLine("Bug\n");
Console.WriteLine("The object is to finish your bug before I finish mine. Each number");
Console.WriteLine("stands for a part of the bug body.\n");
Console.WriteLine("I will roll the die for you, tell you what I rolled for you, what the");
Console.WriteLine("number stands for, and if you can get the part. If you can get the");
Console.WriteLine("part I will give it to you. The same will happen on my turn.\n");
Console.WriteLine("If there is a change in either bug I will give you the option of");
Console.WriteLine("seeing the pictures of the bugs. The numbers stand for parts as");
Console.WriteLine("follows:\n");
PrintPartsTable();
Console.Write("\nPress Enter to start. ");
Console.ReadKey();
}
// Print the given bug.
//
static void PrintBug(ABug bug)
{
if (bug.feelers > 0)
{
for (int i = 0; i < FEELER_LENGTH; i++)
{
Console.Write(" ");
for (int j = 0; j < bug.feelers; j++)
{
Console.Write($" {bug.feelerType}");
}
Console.WriteLine();
}
}
if (bug.head)
{
Console.WriteLine(" HHHHHHH");
Console.WriteLine(" H H");
Console.WriteLine(" H O O H");
Console.WriteLine(" H H");
Console.WriteLine(" H V H");
Console.WriteLine(" HHHHHHH");
}
if (bug.neck)
{
for (int i = 0; i < NECK_LENGTH; i++)
{
Console.WriteLine(" N N");
}
}
if (bug.body)
{
Console.WriteLine(" BBBBBBBBBBBB");
for (int i = 0; i < BODY_HEIGHT; i++)
{
Console.WriteLine(" B B");
}
if (bug.tail)
{
Console.WriteLine("TTTTTB B");
}
Console.WriteLine(" BBBBBBBBBBBB");
}
if (bug.legs > 0)
{
for (int i = 0; i < LEG_LENGTH; i++)
{
Console.Write(" ");
for (int j = 0; j < bug.legs; j++)
{
Console.Write(" L");
}
Console.WriteLine();
}
}
}
// Return `true` if the given bug is finished; otherwise return `false`.
//
static bool Finished(ABug bug)
{
return bug.feelers == MAX_FEELERS && bug.tail && bug.legs == MAX_LEGS;
}
// Return a random number from 1 to 6 (inclusive).
//
static int Dice()
{
Random rand = new Random();
return rand.Next(6) + 1;
}
// Array to convert a number to its equilavent text.
//
static string[] asText =
{
"no",
"a",
"two",
"three",
"four",
"five",
"six" }; // MAX_LEGS
// Return a string containing the given number and noun in their proper
// form.
//
static string Plural(int number, string noun)
{
return $"{asText[number]} {noun}{((number > 1) ? "s" : "")}";
}
// Add the given part to the given player's bug.
//
static bool AddPart(Part part, Player player)
{
bool changed = false;
switch (part)
{
case Part.body:
if (player.bug.body)
{
Console.WriteLine($", but {player.pronoun} already have a body.");
}
else
{
Console.WriteLine($"; {player.pronoun} now have a body:");
player.bug.body = true;
changed = true;
}
break;
case Part.neck:
if (player.bug.neck)
{
Console.WriteLine($", but {player.pronoun} already have a neck.");
}
else if (!player.bug.body)
{
Console.WriteLine($", but {player.pronoun} need a body first.");
}
else
{
Console.WriteLine($"; {player.pronoun} now have a neck:");
player.bug.neck = true;
changed = true;
}
break;
case Part.head:
if (player.bug.head)
{
Console.WriteLine($", but {player.pronoun} already have a head.");
}
else if (!player.bug.neck)
{
Console.WriteLine($", but {player.pronoun} need a a neck first.");
}
else
{
Console.WriteLine($"; {player.pronoun} now have a head:");
player.bug.head = true;
changed = true;
}
break;
case Part.feeler:
if (player.bug.feelers == MAX_FEELERS)
{
Console.WriteLine($", but {player.pronoun} have two feelers already.");
}
else if (!player.bug.head)
{
Console.WriteLine($", but {player.pronoun} need a head first.");
}
else
{
player.bug.feelers += 1;
Console.Write($"; {player.pronoun} now have",
Plural(player.bug.feelers, "feeler"));
Console.WriteLine(":");
changed = true;
}
break;
case Part.tail:
if (player.bug.tail)
{
Console.WriteLine($", but {player.pronoun} already have a tail.");
}
else if (!player.bug.body)
{
Console.WriteLine($", but {player.pronoun} need a body first.");
}
else
{
Console.WriteLine($"; {player.pronoun} now have a tail:");
player.bug.tail = true;
changed = true;
}
break;
case Part.leg:
if (player.bug.legs == MAX_LEGS)
{
Console.WriteLine($", but {player.pronoun} have {asText[MAX_LEGS]} feet already.");
}
else if (!player.bug.body)
{
Console.WriteLine($", but {player.pronoun} need a body first.");
}
else
{
player.bug.legs += 1;
Console.Write($"; {player.pronoun} now have {Plural(player.bug.legs, "leg")}");
Console.WriteLine(":");
changed = true;
}
break;
}
return changed;
}
// Ask the user to press the Enter key, wait for the input, then erase the;
// prompt text.
//
static void Prompt()
{
Console.Write("Press Enter to roll the dice. ");
Console.ReadKey();
}
// Play one turn for the given player, rolling the dice and updating his bug.
//
static void Turn(Player player)
{
Prompt();
int number = Dice();
Part part = (Part) number;
Console.Write($"{Capitalized(player.pronoun)} rolled a {number} ({part})");
if (AddPart(part, player))
{
Console.WriteLine();
PrintBug(player.bug);
}
Console.WriteLine();
}
// Print a message about the winner.
//
static void PrintWinner()
{
if (Finished(human.bug) && Finished(computer.bug))
{
Console.WriteLine("Both of our bugs are finished in the same number of turns!");
}
else if (Finished(human.bug))
{
Console.WriteLine($"{human.possessive} bug is finished.");
}
else if (Finished(computer.bug))
{
Console.WriteLine($"{computer.possessive} bug is finished.");
}
}
static bool GameOver()
{
return Finished(human.bug) || Finished(computer.bug);
}
// Execute the game loop.
//
static void Play()
{
Console.Clear();
while (!GameOver())
{
Turn(human);
Turn(computer);
}
PrintWinner();
}
// Init the players' data before a new game.
//
static void Init()
{
human.pronoun = "you";
human.possessive = "Your";
human.bug.feelerType = 'A';
computer.pronoun = "I";
computer.possessive = "My";
computer.bug.feelerType = 'F';
}
static void Main()
{
Init();
PrintCredits();
PrintInstructions();
Play();
Console.WriteLine("I hope you enjoyed the game, play it again soon!!");
}
}
Bunny
// Bunny
// Original version in BASIC:
// Anonymous, 1978.
// Creative Computing's BASIC Games.
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/bunny.bas
// - http://www.retroarchive.org/cpm/games/ccgames.zip
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-20.
//
// Last modified: 20251205T1532+0100.
using System;
class Bunny
{
static void PrintCredits()
{
Console.WriteLine("Bunny\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Creative Computing (Morristown, New Jersey, USA), 1978.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
Console.Write("Press Enter to start. ");
Console.ReadKey();
}
const int WIDTH = 53;
static char[] line = new char[WIDTH]; // buffer
static void clearLine()
{
for (int c = 0; c < WIDTH; c++)
{
line[c] = ' ';
}
}
static char[] letter = {'B', 'U', 'N', 'N', 'Y'};
const int EOL = -1; // end of line identifier
static 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 };
static int dataIndex = 0;
static int datum()
{
dataIndex += 1;
return data[dataIndex - 1];
}
static void Draw()
{
clearLine();
while (dataIndex < data.Length)
{
int firstColumn = datum();
if (firstColumn == EOL)
{
Console.WriteLine(line);
clearLine();
}
else
{
int lastColumn = datum();
for (int c = firstColumn; c <= lastColumn; c++)
{
line[c] = letter[c % letter.Length];
}
}
}
}
static void Main()
{
Console.Clear();
PrintCredits();
Console.Clear();
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 C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2025-01-01.
//
// Last modified 20251205T1534+0100.
using System;
using System.Linq;
class Chase
{
// Globals {{{1
// =============================================================
// Colors
const ConsoleColor DEFAULT_INK = ConsoleColor.White;
const ConsoleColor INPUT_INK = ConsoleColor.Green;
const ConsoleColor INSTRUCTIONS_INK = ConsoleColor.DarkYellow;
const ConsoleColor TITLE_INK = ConsoleColor.Red;
// Arena
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 = 2;
static string[,] arena = new string[ARENA_HEIGHT, ARENA_WIDTH];
const string EMPTY = " ";
const string FENCE = "X";
const string MACHINE = "m";
const string HUMAN = "@";
const int FENCES = 15; // inner obstacles, not the border
// The end
enum End
{
NotYet,
Quit,
Electrified,
Killed,
Victory,
}
static End theEnd;
// Machines
const int MACHINES = 5;
const int MACHINES_DRAG = 2; // probability not moving: 0=0%, 1=50%, 2=66%, 3=75%, etc.
static int[] machineX = new int[MACHINES];
static int[] machineY = new int[MACHINES];
static bool[] operative = new bool[MACHINES];
static int destroyedMachines; // counter
// Human
static int humanX;
static int humanY;
// Random numbers generator
static Random rand = new Random();
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters a string.
//
static string InputString(string prompt = "")
{
Console.ForegroundColor = INPUT_INK;
Console.Write(prompt);
string result = Console.ReadLine();
Console.ForegroundColor = DEFAULT_INK;
return result;
}
// Print the given prompt and wait until the user presses Enter.
//
static void PressEnter(string prompt)
{
InputString(prompt);
}
// Return `true` if the given string is "yes" or a synonym.
//
static bool IsYes(string s)
{
string[] validOptions = {"ok", "y", "yeah", "yes"};
return validOptions.Contains(s.ToLower());
}
// Return `true` if the given string is "no" or a synonym.
//
static bool IsNo(string s)
{
string[] validOptions = {"n", "no", "nope"};
return validOptions.Contains(s);
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
static bool Yes(string prompt)
{
while (true)
{
string answer = InputString(prompt);
if (IsYes(answer))
{
return true;
}
if (IsNo(answer))
{
return false;
}
}
}
// Title, credits and instructions {{{1
// =============================================================
const string TITLE = "Chase";
// Print the title at the current cursor position.
//
static void PrintTitle()
{
Console.ForegroundColor = TITLE_INK;
Console.WriteLine(TITLE);
Console.ForegroundColor = DEFAULT_INK;
}
static void PrintCredits()
{
PrintTitle();
Console.WriteLine("\nOriginal version in BASIC:");
Console.WriteLine(" Anonymous.");
Console.WriteLine(" Published in \"The Best of Creative Computing\", Volume 2, 1977.");
Console.WriteLine(" https://www.atariarchives.org/bcc2/showpage.php?page=253");
Console.WriteLine("\nThis version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
}
// Print the game instructions and wait for a key.
//
static void PrintInstructions()
{
PrintTitle();
Console.ForegroundColor = INSTRUCTIONS_INK;
Console.WriteLine($"\nYou ({HUMAN}) are in a high voltage maze with {MACHINES}");
Console.WriteLine($"security machines ({MACHINE}) trying to kill you.");
Console.WriteLine($"You must maneuver them into the maze ({FENCE}) to survive.\n");
Console.WriteLine("Good luck!\n");
Console.WriteLine("The movement commands are the following:\n");
Console.WriteLine(" ↖ ↑ ↗");
Console.WriteLine(" NW N NE");
Console.WriteLine(" ← W E →");
Console.WriteLine(" SW S SE");
Console.WriteLine(" ↙ ↓ ↘");
Console.WriteLine("\nPlus 'Q' to end the game.");
Console.ForegroundColor = DEFAULT_INK;
}
// Arena {{{1
// =============================================================
// Display the arena at the top left corner of the screen.
//
static void PrintArena()
{
Console.SetCursorPosition(0, ARENA_ROW);
for (int y = 0; y <= ARENA_LAST_Y; y++)
{
for (int x = 0; x <= ARENA_LAST_X; x++)
{
Console.Write(arena[y, x]);
}
Console.WriteLine();
}
}
// If the given arena coordinates `y` and `x` are part of the border arena
// (i.e. its surrounding fence), return `true`, otherwise return `false`.
//
static bool IsBorder(int y, int x)
{
return (y == 0) || (x == 0) || (y == ARENA_LAST_Y) || (x == ARENA_LAST_X);
}
// Return a random integer in the given inclusive range.
//
static int RandomIntInInclusiveRange(int min, int max)
{
return rand.Next(max - min + 1) + min;
}
// Place the given string at a random empty position of the arena and return
// the coordinates.
//
static (int y, int x) Place(string s)
{
int y;
int x;
while (true)
{
y = RandomIntInInclusiveRange(1, ARENA_LAST_Y - 1);
x = RandomIntInInclusiveRange(1, ARENA_LAST_X - 1);
if (arena[y, x] == EMPTY)
{
break;
}
}
arena[y, x] = s;
return (y, x);
}
// Inhabit the arena with the machines, the inner fences and the human.
//
static void InhabitArena()
{
for (int m = 0; m < MACHINES; m++)
{
(machineY[m], machineX[m]) = Place(MACHINE);
operative[m] = true;
}
for (int _ = 0; _ < FENCES; _++) Place(FENCE);
(humanY, humanX) = Place(HUMAN);
}
// Clean the arena, i.e. empty it and add a surrounding fence.
//
static void CleanArena()
{
for (int y = 0; y <= ARENA_LAST_Y; y++)
{
for (int x = 0; x <= ARENA_LAST_X; x++)
{
arena[y, x] = IsBorder(y, x) ? FENCE : EMPTY;
}
}
}
// Game {{{1
// =============================================================
// Init the game.
//
static void InitGame()
{
CleanArena();
InhabitArena();
destroyedMachines = 0;
theEnd = End.NotYet;
}
// Move the given machine.
//
static void MoveMachine(int m)
{
int maybe;
arena[machineY[m], machineX[m]] = EMPTY;
maybe = rand.Next(2);
if (machineY[m] > humanY)
{
machineY[m] -= maybe;
}
else if (machineY[m] < humanY)
{
machineY[m] += maybe;
}
maybe = rand.Next(2);
if ((machineX[m] > humanX))
{
machineX[m] -= maybe;
}
else if ((machineX[m] < humanX))
{
machineX[m] += maybe;
}
if (arena[machineY[m], machineX[m]] == EMPTY)
{
arena[machineY[m], machineX[m]] = MACHINE;
}
else if (arena[machineY[m], machineX[m]] == FENCE)
{
operative[m] = false;
destroyedMachines += 1;
if (destroyedMachines == MACHINES)
{
theEnd = End.Victory;
}
}
else if (arena[machineY[m], machineX[m]] == HUMAN)
{
theEnd = End.Killed;
}
}
// Maybe move the given operative machine.
//
static void MaybeMoveMachine(int m)
{
if (rand.Next(MACHINES_DRAG) == 0)
{
MoveMachine(m);
}
}
// Move the operative machines.
//
static void MoveMachines()
{
for (int m = 0; m < MACHINES; m++)
{
if (operative[m])
{
MaybeMoveMachine(m);
}
}
}
// Erase the current line to the right from the position of the cursor.
//
static void EraseLineRight()
{
int y = Console.CursorTop;
int x = Console.CursorLeft;
string blank = new String(' ', Console.WindowWidth - x - 1);
Console.Write(blank);
Console.SetCursorPosition(x, y);
}
// Read a user command; update `theEnd` accordingly and return the direction
// increments.
//
static (int yInc, int xInc) GetMove()
{
int yInc = 0;
int xInc = 0;
Console.WriteLine();
EraseLineRight();
string command = InputString("Command: ").ToLower();
switch (command)
{
case "q" : theEnd = End.Quit; break;
case "sw" : yInc = +1; xInc = -1; break;
case "s" : yInc = +1; break;
case "se" : yInc = +1; xInc = +1; break;
case "w" : xInc = -1; break;
case "e" : xInc = +1; break;
case "nw" : yInc = -1; xInc = -1; break;
case "n" : yInc = -1; break;
case "ne" : yInc = -1; xInc = +1; break;
}
return (yInc, xInc);
}
static void Play()
{
int yInc;
int xInc;
while (true) { // game loop
Console.Clear();
PrintTitle();
InitGame();
while (true) { // action loop
PrintArena();
(yInc, xInc) = GetMove();
if (theEnd == End.NotYet)
{
if (yInc != 0 || xInc != 0)
{
arena[humanY, humanX] = EMPTY;
if (arena[humanY + yInc, humanX + xInc] == FENCE)
{
theEnd = End.Electrified;
}
else if (arena[humanY + yInc, humanX + xInc] == MACHINE)
{
theEnd = End.Killed;
}
else
{
arena[humanY, humanX] = EMPTY;
humanY = humanY + yInc;
humanX = humanX + xInc;
arena[humanY, humanX] = HUMAN;
PrintArena();
MoveMachines();
}
}
}
if (theEnd != End.NotYet)
{
break;
}
}// action loop;
switch (theEnd)
{
case End.Quit :
Console.WriteLine("\nSorry to see you quit.");
break;
case End.Electrified :
Console.WriteLine("\nZap! You touched the fence!");
break;
case End.Killed :
Console.WriteLine("\nYou have been killed by a lucky machine.");
break;
case End.Victory :
Console.WriteLine("\nYou are lucky, you destroyed all machines.");
break;
}
if (! Yes("\nDo you want to play again? "))
{
break;
}
}; // game loop
Console.WriteLine("\nHope you don't feel fenced in.");
Console.WriteLine("Try again sometime.");
}
// Main {{{1
// =============================================================
static void Main()
{
Console.ForegroundColor = DEFAULT_INK;
Console.Clear();
PrintCredits();
PressEnter("\nPress the Enter key to read the instructions. ");
Console.Clear();
PrintInstructions();
PressEnter("\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 C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-20.
//
// Last modified 20251205T1534+0100.
using System;
class Diamond
{
static void Main()
{
const int LINES = 17;
int i = 1;
int j = 0;
i = 1;
while (i <= LINES / 2 + 1)
{
j = 1;
while (j <= (LINES + 1) / 2 - i + 1)
{
Console.Write(" ");
j += 1;
}
j = 1;
while (j <= i * 2 - 1)
{
Console.Write("*");
j += 1;
}
Console.WriteLine();
i += 1;
}
i = 1;
while (i <= LINES / 2)
{
j = 1;
while (j <= i + 1)
{
Console.Write(" ");
j += 1;
}
j = 1;
while (j <= ((LINES + 1) / 2 - i) * 2 - 1)
{
Console.Write("*");
j += 1;
}
Console.WriteLine();
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.
// - http://web.archive.org/web/20150215091831/http://www.dunnington.u-net.com/public/basicgames/
// - http://web.archive.org/web/20150120012304/http://www.dunnington.u-net.com/public/basicgames/HMRABI
//
// Modernized versions:
// - https://archive.org/details/hamurabi.qb64
//
// More details:
// - https://en.wikipedia.org/wiki/Hamurabi_(video_game)
// - https://www.mobygames.com/game/22232/hamurabi/
// This improved remake in C#:
// Copyright (c) 2025, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2025-01-01.
//
// Last modified: 20251205T1535+0100.
//
// Acknowledgment:
// The following Python port was used as a reference of the original
// variables: <https://github.com/jquast/hamurabi.py>.
using System;
class hammurabi
{
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 `showIrritation`
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 ConsoleColor DEFAULT_INK = ConsoleColor.White;
const ConsoleColor INPUT_INK = ConsoleColor.Green;
const ConsoleColor INSTRUCTIONS_INK = ConsoleColor.DarkYellow;
const ConsoleColor RESULT_INK = ConsoleColor.Cyan;
const ConsoleColor SPEECH_INK = ConsoleColor.Magenta;
const ConsoleColor TITLE_INK = ConsoleColor.White;
const ConsoleColor WARNING_INK = ConsoleColor.Red;
enum Result
{
VeryGood,
NotTooBad,
Bad,
VeryBad,
}
static int acres;
static int bushelsEatenByRats;
static int bushelsHarvested;
static int bushelsHarvestedPerAcre;
static int bushelsInStore;
static int bushelsToFeedWith;
static int dead;
static int infants;
static int irritation; // counter from 0 to 99 (inclusive)
static int population;
static int starvedPeoplePercentage;
static int totalDead;
static Random rand = new Random();
// Return a random number from 1 to 5 (inclusive).
//
static int Random1To5()
{
return rand.Next(5) + 1;
}
// Return the proper wording for `n` persons, using the given or default words
// for singular and plural forms.
//
static string persons
(
int n,
string singular = "person",
string plural = "people"
)
{
switch (n)
{
case 0:
return "nobody";
case 1:
return $"one {singular}";
default:
return $"{n} {plural}";
}
}
static void PrintInstructions()
{
Console.ForegroundColor = INSTRUCTIONS_INK;
Console.WriteLine("Hammurabi is a simulation game in which you, as the ruler of the ancient");
Console.WriteLine("kingdom of Sumeria, Hammurabi, manage the resources.");
Console.WriteLine("\nYou may buy and sell land with your neighboring city-states for bushels of");
Console.WriteLine($"grain ― the price will vary between {MIN_HARVESTED_BUSHELS_PER_ACRE} and {MAX_HARVESTED_BUSHELS_PER_ACRE} bushels per acre. You also must");
Console.WriteLine("use grain to feed your people and as seed to plant the next year's crop");
Console.WriteLine("\nYou will quickly find that a certain number of people can only tend a certain");
Console.WriteLine("amount of land and that people starve if they are not fed enough. You also");
Console.WriteLine("have the unexpected to contend with such as a plague, rats destroying stored");
Console.WriteLine("grain, and variable harvests.");
Console.WriteLine("\nYou will also find that managing just the few resources in this game is not a");
Console.WriteLine("trivial job. The crisis of population density rears its head very rapidly.");
Console.WriteLine($"\nTry your hand at governing ancient Sumeria for a {YEARS}-year term of office.");
Console.ForegroundColor = DEFAULT_INK;
}
static void PressEnter(string prompt = "> ")
{
InputString(prompt);
}
// Print the given prompt in the correspondent color, wait until the user
// enters a string and return.
//
static string InputString(string prompt = "")
{
Console.ForegroundColor = INPUT_INK;
Console.Write(prompt);
string result = Console.ReadLine();
Console.ForegroundColor = DEFAULT_INK;
return result;
}
// Print the given prompt in the correspondent color, wait until the user
// enters a valid integer and return.
//
static int InputInt(string prompt = "")
{
Console.ForegroundColor = INPUT_INK;
int number;
while (true)
{
Console.Write(prompt);
try
{
number = (int) Int64.Parse(Console.ReadLine());
break;
}
catch
{
Console.WriteLine("Integer expected.");
}
}
Console.ForegroundColor = DEFAULT_INK;
return number;
}
static void PrintCredits()
{
Console.ForegroundColor = TITLE_INK;
Console.WriteLine("Hammurabi");
Console.WriteLine("\nOriginal program:");
Console.WriteLine(" Written in FOCAL on a DEP PDP-8 by Rick Merrill, 1969.");
Console.WriteLine("\nBASIC port:");
Console.WriteLine(" Ported from FOCAL and modified for Edusystem 70 by David Ahl, c. 1973.");
Console.WriteLine(" Modified for 8K Microsoft BASIC by Peter Turnbull, c. 1978.");
Console.WriteLine("\nThis improved remake in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
Console.ForegroundColor = DEFAULT_INK;
}
static string OrdinalSuffix(int n)
{
switch (n)
{
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
// Return the description of the given year as the previous one.
//
static string Previous(int year)
{
if (year == 0)
{
return "the previous year";
}
else
{
return $"your {year}{OrdinalSuffix(year)} year";
}
}
static void PrintAnnualReport(int year)
{
Console.Clear();
Console.ForegroundColor = SPEECH_INK;
Console.WriteLine("Hammurabi, I beg to report to you.");
Console.ForegroundColor = DEFAULT_INK;
Console.WriteLine
(
"\nIn {0}, {1} starved and {2} {3} born.",
Previous(year),
persons(dead),
persons(infants, "infant", "infants"),
infants > 1 ? "were" : "was"
);
population += infants;
double plagueChance = rand.NextDouble();
if (year > 0 ? plagueChance <= PLAGUE_CHANCE : false)
{
population = (int) population / 2;
Console.ForegroundColor = WARNING_INK;
Console.WriteLine("A horrible plague struck! Half the people died.");
Console.ForegroundColor = DEFAULT_INK;
}
Console.WriteLine($"The population is {population}.");
Console.WriteLine($"The city owns {acres} acres.");
Console.WriteLine
(
"You harvested {0} bushels ({1} per acre).",
bushelsHarvested,
bushelsHarvestedPerAcre
);
if (bushelsEatenByRats > 0)
{
Console.WriteLine($"The rats ate {bushelsEatenByRats} bushels.");
}
Console.WriteLine($"You have {bushelsInStore} bushels in store.");
bushelsHarvestedPerAcre =
(int) (RANGE_OF_HARVESTED_BUSHELS_PER_ACRE * rand.NextDouble()) +
MIN_HARVESTED_BUSHELS_PER_ACRE;
Console.WriteLine($"Land is trading at {bushelsHarvestedPerAcre} bushels per acre.\n");
}
static void SayBye()
{
Console.ForegroundColor = DEFAULT_INK;
Console.WriteLine("\nSo long for now.\n");
}
static void QuitGame()
{
SayBye();
Environment.Exit(0);
}
static void Relinquish()
{
Console.ForegroundColor = SPEECH_INK;
Console.WriteLine("\nHammurabi, I am deeply irritated and cannot serve you anymore.");
Console.WriteLine("Please, get yourself another steward!");
Console.ForegroundColor = DEFAULT_INK;
QuitGame();
}
static void IncreaseIrritation()
{
irritation += 1 + rand.Next(IRRITATION_STEP);
if (irritation >= MAX_IRRITATION)
{
Relinquish();
} // this never returns
}
static void PrintIrritated(string adverb)
{
Console.WriteLine($"The steward seems {adverb} irritated.");
}
static void ShowIrritation()
{
if (irritation < IRRITATION_STEP || irritation < IRRITATION_STEP * 2)
{
PrintIrritated("slightly");
}
else if (irritation < IRRITATION_STEP * 3)
{
PrintIrritated("quite");
}
else if (irritation < IRRITATION_STEP * 4)
{
PrintIrritated("very");
}
else
{
PrintIrritated("profoundly");
}
}
// Print a message begging to repeat an ununderstandable input.
//
static void BegRepeat()
{
IncreaseIrritation(); // this may never return
Console.ForegroundColor = SPEECH_INK;
Console.WriteLine("I beg your pardon? I did not understand your order.");
Console.ForegroundColor = DEFAULT_INK;
ShowIrritation();
}
// Print a message begging to repeat a wrong input, because there's only `n`
// items of `name`.
//
static void BegThinkAgain(int n, string name)
{
IncreaseIrritation(); // this may never return
Console.ForegroundColor = SPEECH_INK;
Console.WriteLine($"I beg your pardon? You have only {n} {name}. Now then…");
Console.ForegroundColor = DEFAULT_INK;
ShowIrritation();
}
// Buy or sell land.
//
static void Trade()
{
int acresToBuy;
int acresToSell;
while (true)
{
acresToBuy = InputInt("How many acres do you wish to buy? (0 to sell): ");
if (acresToBuy < 0)
{
BegRepeat(); // this may never return
continue;
}
if (bushelsHarvestedPerAcre * acresToBuy <= bushelsInStore)
{
break;
}
BegThinkAgain(bushelsInStore, "bushels of grain");
}
if (acresToBuy != 0)
{
Console.WriteLine($"You buy {acresToBuy} acres.");
acres += acresToBuy;
bushelsInStore -= bushelsHarvestedPerAcre * acresToBuy;
Console.WriteLine($"You now have {acres} acres and {bushelsInStore} bushels.");
}
else
{
while (true)
{
acresToSell = InputInt("How many acres do you wish to sell?: ");
if (acresToSell < 0)
{
BegRepeat(); // this may never return
continue;
}
if (acresToSell < acres)
{
break;
}
BegThinkAgain(acres, "acres");
}
if (acresToSell > 0)
{
Console.WriteLine($"You sell {acresToSell} acres.");
acres -= acresToSell;
bushelsInStore += bushelsHarvestedPerAcre * acresToSell;
Console.WriteLine($"You now have {acres} acres and {bushelsInStore} bushels.");
}
}
}
// Feed the people.
//
static void Feed()
{
while (true)
{
bushelsToFeedWith = InputInt("How many bushels do you wish to feed your people with?: ");
if (bushelsToFeedWith < 0)
{
BegRepeat(); // this may never return
continue;
}
// Trying to use more grain than is in silos?
if (bushelsToFeedWith <= bushelsInStore)
{
break;
}
BegThinkAgain(bushelsInStore, "bushels of grain");
}
Console.WriteLine($"You feed your people with {bushelsToFeedWith} bushels.");
bushelsInStore -= bushelsToFeedWith;
Console.WriteLine($"You now have {bushelsInStore} bushels");
}
// Seed the land.
//
static void Seed()
{
int acresToSeed;
while (true)
{
acresToSeed = InputInt("How many acres do you wish to seed?: ");
if (acresToSeed < 0)
{
BegRepeat(); // this may never return
continue;
}
if (acresToSeed == 0)
{
break;
}
// Trying to seed more acres than you own?
if (acresToSeed > acres)
{
BegThinkAgain(acres, "acres");
continue;
}
// Enough grain for seed?
if ((int) acresToSeed / ACRES_A_BUSHEL_CAN_SEED > bushelsInStore)
{
BegThinkAgain
(
bushelsInStore,
$"bushels of grain,\nand one bushel can seed {ACRES_A_BUSHEL_CAN_SEED} acres"
);
continue;
}
// Enough people to tend the crops?
if (acresToSeed <= ACRES_A_PERSON_CAN_SEED * population)
{
break;
}
BegThinkAgain
(
population,
$"people to tend the fields,\nand one person can seed {ACRES_A_PERSON_CAN_SEED} acres"
);
}
int bushelsUsedForSeeding = (int) acresToSeed / ACRES_A_BUSHEL_CAN_SEED;
Console.WriteLine($"You seed {acresToSeed} acres using {bushelsUsedForSeeding} bushels.");
bushelsInStore -= bushelsUsedForSeeding;
Console.WriteLine($"You now have {bushelsInStore} bushels.");
// A bountiful harvest!
bushelsHarvestedPerAcre = Random1To5();
bushelsHarvested = acresToSeed * bushelsHarvestedPerAcre;
bushelsInStore += bushelsHarvested;
}
static bool IsEven(int n)
{
return n % 2 == 0;
}
static void CheckRats()
{
int ratChance = Random1To5();
bushelsEatenByRats = IsEven(ratChance) ? (int) bushelsInStore / ratChance : 0;
bushelsInStore -= bushelsEatenByRats;
}
// Set the variables to their values in the first year.
//
static void Init()
{
dead = 0;
totalDead = 0;
starvedPeoplePercentage = 0;
population = 95;
infants = 5;
acres = ACRES_PER_PERSON * (population + infants);
bushelsHarvestedPerAcre = 3;
bushelsHarvested = acres * bushelsHarvestedPerAcre;
bushelsEatenByRats = 200;
bushelsInStore = bushelsHarvested - bushelsEatenByRats;
irritation = 0;
}
static void PrintResult(Result result)
{
Console.ForegroundColor = RESULT_INK;
switch (result)
{
case Result.VeryGood :
Console.WriteLine("A fantastic performance! Charlemagne, Disraeli and Jefferson combined could");
Console.WriteLine("not have done better!");
break;
case Result.NotTooBad :
Console.WriteLine("Your performance could have been somewat better, but really wasn't too bad at");
Console.WriteLine
(
"all. {0} people would dearly like to see you assassinated, but we all have our",
(int) ((double) population * .8 * rand.NextDouble())
);
Console.WriteLine("trivial problems.");
break;
case Result.Bad :
Console.WriteLine("Your heavy-handed performance smacks of Nero and Ivan IV. The people");
Console.WriteLine("(remaining) find you an unpleasant ruler and, frankly, hate your guts!");
break;
case Result.VeryBad :
Console.WriteLine("Due to this extreme mismanagement you have not only been impeached and thrown");
Console.WriteLine("out of office but you have also been declared national fink!!!");
break;
}
Console.ForegroundColor = DEFAULT_INK;
}
static void PrintFinalReport()
{
Console.Clear();
if (starvedPeoplePercentage > 0)
{
Console.WriteLine
(
"In your {0}-year term of office, {1} percent of the",
YEARS,
starvedPeoplePercentage
);
Console.WriteLine
(
"population starved per year on the average, i.e., a total of {0} people died!\n",
totalDead
);
}
int acresPerPerson = acres / population;
Console.WriteLine
(
"You started with {0} acres per person and ended with {1}.\n",
ACRES_PER_PERSON,
acresPerPerson
);
if (starvedPeoplePercentage > 33 || acresPerPerson < 07)
{
PrintResult(Result.VeryBad);
}
else if (starvedPeoplePercentage > 10 || acresPerPerson < 09)
{
PrintResult(Result.Bad);
}
else if (starvedPeoplePercentage > 03 || acresPerPerson < 10)
{
PrintResult(Result.NotTooBad);
}
else
{
PrintResult(Result.VeryGood);
}
}
static void CheckStarvation(int year)
{
// How many people has been fed?
int fedPeople = (int) bushelsToFeedWith / BUSHELS_TO_FEED_A_PERSON;
if (population > fedPeople)
{
dead = population - fedPeople;
starvedPeoplePercentage = ((year - 1) * starvedPeoplePercentage + dead * 100 / population) / year;
population -= dead;
totalDead += dead;
// Starve enough for impeachment?
if (dead > (int) (.45 * (double) population))
{
Console.ForegroundColor = WARNING_INK;
Console.WriteLine($"\nYou starved {dead} people in one year!!!\n");
Console.ForegroundColor = DEFAULT_INK;
PrintResult(Result.VeryBad);
QuitGame();
}
}
}
static void Govern()
{
Init();
PrintAnnualReport(0);
for (int year = 1; year <= YEARS; year++)
{
Trade();
Feed();
Seed();
CheckRats();
// Let's have some babies
infants = (int) (Random1To5() * (20 * acres + bushelsInStore) / population / 100 + 1);
CheckStarvation(year);
PressEnter("\nPress the Enter key to read the annual report. ");
PrintAnnualReport(year);
}
}
static void Main()
{
Console.Clear();
PrintCredits();
PressEnter("\nPress the Enter key to read the instructions. ");
Console.Clear();
PrintInstructions();
PressEnter("\nPress the Enter key to start. ");
Govern();
PressEnter("Press the Enter key to read the final report. ");
PrintFinalReport();
SayBye();
}
}
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 C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-25/26.
//
// Last modified: 20251205T1540+0100.
using System;
using System.Linq;
class HighNoon
{
static Random rand = new Random();
// Global variables and constants {{{1
// =============================================================
const ConsoleColor DEFAULT_COLOR = ConsoleColor.White;
const ConsoleColor INPUT_COLOR = ConsoleColor.Green;
const ConsoleColor INSTRUCTIONS_COLOR = ConsoleColor.DarkYellow;
const ConsoleColor TITLE_COLOR = ConsoleColor.Red;
const int INITIAL_DISTANCE = 100;
const int INITIAL_BULLETS = 4;
const int MAX_WATERING_TROUGHS = 3;
static int distance; // distance between both gunners, in paces
static string strategy; // player's strategy
static int playerBullets;
static int opponentBullets;
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters a string.
//
static string GetString(string prompt = "")
{
Console.ForegroundColor = INPUT_COLOR;
Console.Write(prompt);
string input = Console.ReadLine();
Console.ForegroundColor = DEFAULT_COLOR;
return input;
}
// Print the given prompt and wait until the user enters an integer.
//
static int GetInteger(string prompt = "")
{
int n = 0;
Console.ForegroundColor = INPUT_COLOR;
while (true)
{
try
{
n = Int32.Parse(GetString(prompt));
break;
}
catch
{
}
}
Console.ForegroundColor = DEFAULT_COLOR;
return n;
}
// Return `true` if the given string is "yes" or a synonym.
//
static bool IsYes(string s)
{
string[] validOptions = {"ok", "y", "yeah", "yes"};
return validOptions.Contains(s.ToLower());
}
// Return `true` if the given string is "no" or a synonym.
//
static bool IsNo(string s)
{
string[] validOptions = {"n", "no", "nope"};
return validOptions.Contains(s.ToLower());
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
static bool Yes(string prompt)
{
while (true)
{
string answer = GetString(prompt);
if (IsYes(answer))
{
return true;
}
if (IsNo(answer))
{
return false;
}
}
}
// Title, instructions and credits {{{1
// =============================================================
// Print the title at the current cursor position.
//
static void PrintTitle()
{
Console.ForegroundColor = TITLE_COLOR;
Console.WriteLine("High Noon");
Console.ForegroundColor = DEFAULT_COLOR;
}
static void PrintCredits()
{
PrintTitle();
Console.WriteLine("\nOriginal version in BASIC:");
Console.WriteLine(" Designed and programmend by Chris Gaylo, 1970.");
Console.WriteLine(" http://mybitbox.com/highnoon-1970/");
Console.WriteLine(" http://mybitbox.com/highnoon/");
Console.WriteLine("Transcriptions:");
Console.WriteLine(" https://github.com/MrMethor/Highnoon-BASIC/");
Console.WriteLine(" https://github.com/mad4j/basic-highnoon/");
Console.WriteLine("Version modified for QB64:");
Console.WriteLine(" By Daniele Olmisani, 2014.");
Console.WriteLine(" https://github.com/mad4j/basic-highnoon/");
Console.WriteLine("This improved remake in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
}
static void PrintInstructions()
{
PrintTitle();
Console.ForegroundColor = INSTRUCTIONS_COLOR;
Console.WriteLine("\nYou have been challenged to a showdown by Black Bart, one of");
Console.WriteLine("the meanest desperadoes west of the Allegheny mountains.");
Console.WriteLine("\nWhile you are walking down a dusty, deserted side street,");
Console.WriteLine("Black Bart emerges from a saloon one hundred paces away.");
Console.Write($"\nBy agreement, you each have {INITIAL_BULLETS} bullets in your six-guns.");
Console.WriteLine("\nYour marksmanship equals his. At the start of the walk nei-");
Console.WriteLine("ther of you can possibly hit the other, and at the end of");
Console.WriteLine("the walk, neither can miss. the closer you get, the better");
Console.WriteLine("your chances of hitting black Bart, but he also has beter");
Console.WriteLine("chances of hitting you.");
Console.ForegroundColor = DEFAULT_COLOR;
}
// Game loop {{{1
// =============================================================
static string PluralSuffix(int n)
{
return n == 1 ? "" : "s";
}
static void PrintShellsLeft()
{
if (playerBullets == opponentBullets)
{
Console.WriteLine($"Both of you have {playerBullets} bullets.");
}
else
{
Console.WriteLine
(
"You now have {0} bullet{1} to Black Bart's {2} bullet{3}.",
playerBullets,
PluralSuffix(playerBullets),
opponentBullets,
PluralSuffix(opponentBullets)
);
}
}
static void PrintCheck()
{
Console.WriteLine("******************************************************");
Console.WriteLine("* *");
Console.WriteLine("* BANK OF DODGE CITY *");
Console.WriteLine("* CASHIER'S RECEIT *");
Console.WriteLine("* *");
Console.WriteLine("* CHECK NO. {0:0000} AUGUST {1}TH, 1889 *",
rand.Next(1000),
10 + rand.Next(10));
Console.WriteLine("* *");
Console.WriteLine("* *");
Console.WriteLine("* PAY TO THE BEARER ON DEMAND THE SUM OF *");
Console.WriteLine("* *");
Console.WriteLine("* TWENTY THOUSAND DOLLARS-------------------$20,000 *");
Console.WriteLine("* *");
Console.WriteLine("******************************************************");
}
static void GetReward()
{
Console.WriteLine("As mayor of Dodge City, and on behalf of its citizens,");
Console.WriteLine("I extend to you our thanks, and present you with this");
Console.WriteLine("reward, a check for $20,000, for killing Black Bart.\n\n");
PrintCheck();
Console.WriteLine("\n\nDon't spend it all in one place.");
}
static void MoveTheOpponent()
{
int paces = 2 + rand.Next(8);
Console.WriteLine($"Black Bart moves {paces} 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.
//
static bool MaybeMoveTheOpponent(bool silent = false)
{
if (rand.Next(2) == 0) { // 50% chances
MoveTheOpponent();
return true;
}
else
{
if (! silent)
{
Console.WriteLine("Black Bart stands still.");
}
return false;
}
}
static bool MissedShot()
{
return rand.NextDouble() * 10 <= (double) 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`.
//
static bool TheOpponentFiresAndKills()
{
Console.WriteLine("Black Bart fires…");
opponentBullets -= 1;
if (MissedShot())
{
Console.WriteLine("A miss…");
switch (opponentBullets)
{
case 3:
Console.WriteLine("Whew, were you lucky. That bullet just missed your head.");
break;
case 2:
Console.WriteLine("But Black Bart got you in the right shin.");
break;
case 1:
Console.WriteLine("Though Black Bart got you on the left side of your jaw.");
break;
case 0:
Console.WriteLine("Black Bart must have jerked the trigger.");
break;
}
}
else
{
if (strategy == "j")
{
Console.WriteLine("That trick just saved yout life. Black Bart's bullet");
Console.WriteLine("was stopped by the wood sides of the trough.");
}
else
{
Console.WriteLine("Black Bart shot you right through the heart that time.");
Console.WriteLine("You went kickin' with your boots on.");
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`.
//
static bool TheOpponentKillsOrRuns()
{
if (distance >= 10 || playerBullets == 0)
{
if (MaybeMoveTheOpponent(silent: true))
{
return false;
}
}
if (opponentBullets > 0)
{
return TheOpponentFiresAndKills();
}
else
{
if (playerBullets > 0)
{
if (rand.Next(2) == 0)
{
{;
} // 50% chances
Console.WriteLine("Now is your chance, Black Bart is out of bullets.");
}
else
{
Console.WriteLine("Black Bart just hi-tailed it out of town rather than face you");
Console.WriteLine("without a loaded gun. You can rest assured that Black Bart");
Console.WriteLine("won't ever show his face around this town again.");
return true;
}
}
}
return false;
}
static void Play()
{
distance = INITIAL_DISTANCE;
int wateringTroughs = 0;
playerBullets = INITIAL_BULLETS;
opponentBullets = INITIAL_BULLETS;
while (true)
{
Console.WriteLine($"You are now {distance} paces apart from Black Bart.");
PrintShellsLeft();
Console.ForegroundColor = INSTRUCTIONS_COLOR;
Console.WriteLine("\nStrategies:");
Console.WriteLine(" [A]dvance");
Console.WriteLine(" [S]tand still");
Console.WriteLine(" [F]ire");
Console.WriteLine(" [J]ump behind the watering trough");
Console.WriteLine(" [G]ive up");
Console.WriteLine(" [T]urn tail and run");
Console.ForegroundColor = DEFAULT_COLOR;
strategy = (GetString("What is your strategy? ")).ToLower();
switch (strategy)
{
case "a": // advance
while (true)
{
int paces = GetInteger("How many paces do you advance? ");
if (paces < 0)
{
Console.WriteLine("None of this negative stuff, partner, only positive numbers.");
}
else if (paces > 10)
{
Console.WriteLine("Nobody can walk that fast.");
}
else
{
distance -= paces;
break;
}
}
break;
case "s": // stand still
Console.WriteLine("That move made you a perfect stationary target.");
break;
case "f": // fire
if (playerBullets == 0)
{
Console.WriteLine("You don't have any bullets left.");
}
else
{
playerBullets -= 1;
if (MissedShot())
{
switch (playerBullets)
{
case 2:
Console.WriteLine("Grazed Black Bart in the right arm.");
break;
case 1:
Console.WriteLine("He's hit in the left shoulder, forcing him to use his right");
Console.WriteLine("hand to shoot with.");
break;
}
Console.WriteLine("What a lousy shot.");
if (playerBullets == 0)
{
Console.WriteLine("Nice going, ace, you've run out of bullets.");
if (opponentBullets != 0)
{
Console.WriteLine("Now Black Bart won't shoot until you touch noses.");
Console.WriteLine("You better think of something fast (like run).");
}
}
}
else
{
Console.WriteLine("What a shot, you got Black Bart right between the eyes.");
GetString("\nPress the Enter key to get your reward. ");
Console.Clear();
GetReward();
return;
}
}
break;
case "j": // jump
if (wateringTroughs == MAX_WATERING_TROUGHS)
{
Console.WriteLine("How many watering troughs do you think are on this street?");
strategy = "";
}
else
{
wateringTroughs += 1;
Console.WriteLine("You jump behind the watering trough.");
Console.WriteLine("Not a bad maneuver to threw Black Bart's strategy off.");
}
break;
case "g": // give up
Console.WriteLine("Black Bart accepts. The conditions are that he won't shoot you");
Console.WriteLine("if you take the first stage out of town and never come back.");
if (Yes("Agreed? "))
{
Console.WriteLine("A very wise decision.");
return;
}
else
{
Console.WriteLine("Oh well, back to the showdown.");
}
break;
case "t": // turn tail and run
// The more bullets of the opponent, the less chances to escape.
if (rand.Next(opponentBullets + 2) == 0)
{
Console.WriteLine("Man, you ran so fast even dogs couldn't catch you.");
}
else
{
switch (opponentBullets)
{
case 0:
Console.WriteLine("You were lucky, Black Bart can only throw his gun at you, he");
Console.WriteLine("doesn't have any bullets left. You should really be dead.");
break;
case 1:
Console.WriteLine("Black Bart fires his last bullet…");
Console.WriteLine("He got you right in the back. That's what you deserve, for running.");
break;
case 2:
Console.WriteLine("Black Bart fires and got you twice: in your back");
Console.WriteLine("and your ass. Now you can't even rest in peace.");
break;
case 3:
Console.WriteLine("Black Bart unloads his gun, once in your back");
Console.WriteLine("and twice in your ass. Now you can't even rest in peace.");
break;
case 4:
Console.WriteLine("Black Bart unloads his gun, once in your back");
Console.WriteLine("and three times in your ass. Now you can't even rest in peace.");
break;
}
opponentBullets = 0;
}
return;
default:
Console.WriteLine("You sure aren't going to live very long if you can't even follow directions.");
break;
}; // strategy switch
if (TheOpponentKillsOrRuns())
{
break;
}
if (playerBullets + opponentBullets == 0)
{
Console.WriteLine("The showdown must end, because nobody has bullets left.");
break;
}
Console.WriteLine();
}; // showdown loop
}
// Main {{{1
// =============================================================
static void Main()
{
Console.Clear();
PrintCredits();
GetString("\nPress the Enter key to read the instructions. ");
Console.Clear();
PrintInstructions();
GetString("\nPress the Enter key to start. ");
Console.Clear();
Play();
}
}
Math
// Math
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-21.
//
// Last modified: 20251205T1540+0100.
using System;
class vintageBasicMath
{
static void Main()
{
double n;
while (true)
{
Console.Write("Enter a number: ");
try
{
// XXX TODO Support any culture-specific number notation.
n = Double.Parse(Console.ReadLine());
break;
}
catch
{
}
}
Console.WriteLine($"ABS({n}) -> Math.Abs({n}) -> {Math.Abs(n)}");
Console.WriteLine($"ATN({n}) -> Math.Atan({n}) -> {Math.Atan(n)}");
Console.WriteLine($"COS({n}) -> Math.Cos({n}) -> {Math.Cos(n)}");
Console.WriteLine($"EXP({n}) -> Math.Exp({n}) -> {Math.Exp(n)}");
Console.WriteLine($"INT({n}) -> (int) {n}) -> {(int) n}");
Console.WriteLine($"LOG({n}) -> Math.Log({n}, Math.E) -> {Math.Log(n, Math.E)}");
Console.WriteLine($"SGN({n}) -> Math.Sign({n}) -> {Math.Sign(n)}");
Console.WriteLine($"SQR({n}) -> Math.Sqrt({n}) -> {Math.Sqrt(n)}");
Console.WriteLine($"TAN({n}) -> Math.Tan({n}) -> {Math.Tan(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
// - http://vintage-basic.net/bcg/mugwump.bas
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-25.
//
// Last modified: 20251205T1542+0100.
using System;
using System.Linq;
class MugwumpGame
{
const int GRID_SIZE = 10;
const int TURNS = 10;
const int MUGWUMPS = 4;
class Mugwump
{
internal int x = 0;
internal int y = 0;
internal bool hidden = false;
}
static Mugwump[] mugwump = new Mugwump[MUGWUMPS];
static int found; // counter
static string GetString(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
// Print the given prompt, wait until the user enters a valid integer and
// return it.
//
static int GetNumber(string prompt)
{
int n = 0;
while (true)
{
try
{
n = Int32.Parse(GetString(prompt));
break;
}
catch
{
}
}
return n;
}
// Return `true` if the given string is "yes" or a synonym.
//
static bool IsYes(string s)
{
string[] validOptions = {"ok", "y", "yeah", "yes"};
return validOptions.Contains(s.ToLower());
}
// Return `true` if the given string is "no" or a synonym.
//
static bool IsNo(string s)
{
string[] validOptions = {"n", "no", "nope"};
return validOptions.Contains(s.ToLower());
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
static bool Yes(string prompt)
{
while (true)
{
string answer = GetString(prompt);
if (IsYes(answer))
{
return true;
}
if (IsNo(answer))
{
return false;
}
}
}
// Clear the screen, print the credits and ask the user to press enter.
//
static void PrintCredits()
{
Console.Clear();
Console.WriteLine("Mugwump\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Written by Bud Valenti's students of Project SOLO (Pittsburg, Pennsylvania, USA).");
Console.WriteLine(" Slightly modified by Bob Albrecht of People's Computer Company.");
Console.WriteLine(" Published by Creative Computing (Morristown, New Jersey, USA), 1978.");
Console.WriteLine(" - https://www.atariarchives.org/basicgames/showpage.php?page=114");
Console.WriteLine(" - http://vintage-basic.net/games.html\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
GetString("Press Enter to read the instructions. ");
}
// Clear the screen, print the instructions and ask the user to press enter.
//
static void PrintInstructions()
{
Console.Clear();
Console.WriteLine("Mugwump\n");
Console.WriteLine("The object of this game is to find four mugwumps");
Console.WriteLine("hidden on a 10 by 10 grid. Homebase is position 0,0.");
Console.WriteLine("Any guess you make must be two numbers with each");
Console.WriteLine("number between 0 and 9, inclusive. First number");
Console.WriteLine("is distance to right of homebase and second number");
Console.WriteLine("is distance above homebase.\n");
Console.WriteLine($"You get {TURNS} tries. After each try, you will see");
Console.WriteLine("how far you are from each mugwump.\n");
GetString("Press Enter to start. ");
}
static void InitMugwumps()
{
for (int m = 0; m < MUGWUMPS; m++)
{
mugwump[m] = new Mugwump();
}
}
// Init the mugwumps' positions, `hidden` flags and count.
//
static void HideMugwumps()
{
Random rand = new Random();
for (int m = 0; m < MUGWUMPS; m++)
{
mugwump[m].x = rand.Next(GRID_SIZE);
mugwump[m].y = rand.Next(GRID_SIZE);
mugwump[m].hidden = true;
}
found = 0; // counter
}
// Print the given prompt, wait until the user enters a valid coord and return
// it.
//
static int GetCoord(string prompt)
{
int coord = 0;
while (true)
{
coord = GetNumber(prompt);
if (coord < 0 || coord >= GRID_SIZE)
{
Console.WriteLine($"Invalid value {coord}: not in range [0, {GRID_SIZE - 1}].");
}
else
{
break;
}
}
return coord;
}
// Return `true` if the given mugwump is hidden in the given coords.
//
static bool IsHere(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.
//
static int Distance(int m, int x, int y)
{
return (int) Math.Sqrt
(
Math.Pow((double) (mugwump[m].x - x), 2) +
Math.Pow((double) (mugwump[m].y - y), 2)
);
}
static string Plural(int n, string plural = "s", string singular = "")
{
return n > 1 ? plural : singular;
}
// Run the game.
//
static void Play()
{
int turn = 1; // counter
InitMugwumps();
while (true) {; // game
Console.Clear();
HideMugwumps();
while (turn <= TURNS)
{
Console.WriteLine($"Turn number {turn}\n");
Console.WriteLine($"What is your guess (in range [0, {GRID_SIZE - 1}])?");
int x = GetCoord("Distance right of homebase (x-axis): ");
int y = GetCoord("Distance above homebase (y-axis): ");
Console.WriteLine($"\nYour guess is ({x}, {y}).");
for (int m = 0; m < MUGWUMPS; m++)
{
if (IsHere(m, x, y))
{
mugwump[m].hidden = false;
found++;
Console.WriteLine($"You have found mugwump {m}!");
if (found == MUGWUMPS)
{
goto endTurns;
}
}
}
for (int m = 0; m < MUGWUMPS; m++)
{
if (mugwump[m].hidden)
{
Console.WriteLine($"You are {Distance(m, x, y)} units from mugwump {m}.");
}
}
Console.WriteLine();
turn++;
}; // turns
endTurns:
if (found == MUGWUMPS)
{
Console.WriteLine($"\nYou got them all in {turn} turn{Plural(turn)}!\n");
Console.WriteLine("That was fun! let's play again…");
Console.WriteLine("Four more mugwumps are now in hiding.");
}
else
{
Console.WriteLine($"\nSorry, that's {TURNS} tr{Plural(TURNS, "ies", "y")}.\n");
Console.WriteLine("Here is where they're hiding:");
for (int m = 0; m < MUGWUMPS; m++)
{
if (mugwump[m].hidden)
{
Console.WriteLine($"Mugwump {m} is at ({mugwump[m].x}, {mugwump[m].y}).");
}
}
}
Console.WriteLine();
if (!Yes("Do you want to play again? "))
{
break;
}
}; // game
}
static void Main()
{
PrintCredits();
PrintInstructions();
Play();
}
}
Name
// Name
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-21.
//
// Last modified: 20251205T1543+0100.
using System;
class Name
{
static void Main()
{
Console.Write("What is your name? ");
string name = Console.ReadLine();
double number;
while (true)
{
Console.Write("Enter a number: ");
try
{
number = Int64.Parse(Console.ReadLine());
break;
}
catch
{
Console.WriteLine("Integer expected.");
}
}
for (int i = 0; i < number; i++)
{
Console.WriteLine($"Hello, {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
// - http://vintage-basic.net/bcg/poetry.bas
// This improved remake in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-24/25.
//
// Last modified: 20251205T1545+0100.
using System;
using System.Threading;
class poetry
{
// Globals {{{1
// =============================================================
const ConsoleColor DEFAULT_COLOR = ConsoleColor.White;
const ConsoleColor INPUT_COLOR = ConsoleColor.Green;
const ConsoleColor TITLE_COLOR = ConsoleColor.Red;
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters a string.
//
static string InputString(string prompt = "")
{
Console.ForegroundColor = INPUT_COLOR;
Console.Write(prompt);
string result = Console.ReadLine();
Console.ForegroundColor = DEFAULT_COLOR;
return result;
}
// Print the given prompt and wait until the user presses Enter.
//
static void PressEnter(string prompt)
{
InputString(prompt);
}
// Title and credits {{{1
// =============================================================
// Print the title at the current cursor position.
//
static void PrintTitle()
{
Console.ForegroundColor = TITLE_COLOR;
Console.WriteLine("Poetry");
Console.ForegroundColor = DEFAULT_COLOR;
}
// Print the credits at the current cursor position.
//
static void PrintCredits()
{
PrintTitle();
Console.WriteLine("\nOriginal version in BASIC:");
Console.WriteLine(" Unknown author.");
Console.WriteLine(" Published in \"BASIC Computer Games\",");
Console.WriteLine(" Creative Computing (Morristown, New Jersey, USA), 1978.\n");
Console.WriteLine("This improved remake in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
}
// Main {{{1
// =============================================================
// Is the given integer even?
//
static bool IsEven(int n)
{
return (n % 2) == 0;
}
static void Play()
{
const int MAX_PHRASES_AND_VERSES = 20;
// counters:
int action = 0;
int phrase = 0;
int phrasesAndVerses = 0;
int verseChunks = 0;
while (true)
{
verse:
bool manageTheVerseContinuation = true;
bool maybeAddComma = true;
switch (action)
{
case 0:
case 1:
switch (phrase)
{
case 0: Console.Write("MIDNIGHT DREARY"); break;
case 1: Console.Write("FIERY EYES"); break;
case 2: Console.Write("BIRD OR FIEND"); break;
case 3: Console.Write("THING OF EVIL"); break;
case 4: Console.Write("PROPHET"); break;
}
break;
case 2:
switch (phrase)
{
case 0:
Console.Write("BEGUILING ME");
verseChunks = 2;
break;
case 1:
Console.Write("THRILLED ME");
break;
case 2:
Console.Write("STILL SITTING…");
maybeAddComma = false;
break;
case 3:
Console.Write("NEVER FLITTING");
verseChunks = 2;
break;
case 4:
Console.Write("BURNED");
break;
}
break;
case 3:
switch (phrase)
{
case 0:
Console.Write("AND MY SOUL");
break;
case 1:
Console.Write("DARKNESS THERE");
break;
case 2:
Console.Write("SHALL BE LIFTED");
break;
case 3:
Console.Write("QUOTH THE RAVEN");
break;
case 4:
if (verseChunks != 0)
{
Console.Write("SIGN OF PARTING");
}
break;
}
break;
case 4:
switch (phrase)
{
case 0: Console.Write("NOTHING MORE"); break;
case 1: Console.Write("YET AGAIN"); break;
case 2: Console.Write("SLOWLY CREEPING"); break;
case 3: Console.Write("…EVERMORE"); break;
case 4: Console.Write("NEVERMORE"); break;
}
break;
case 5:
action = 0;
Console.WriteLine();
if (phrasesAndVerses > MAX_PHRASES_AND_VERSES)
{
Console.WriteLine();
verseChunks = 0;
phrasesAndVerses = 0;
action = 2;
goto verse;
}
else
{
manageTheVerseContinuation = false;
}
break;
}
Random rand = new Random();
if (manageTheVerseContinuation)
{
Thread.Sleep(250); // milliseconds
if (maybeAddComma && ! (verseChunks == 0 || rand.NextDouble() > 0.19))
{
Console.Write(",");
verseChunks = 2;
}
if (rand.NextDouble() > 0.65)
{
Console.WriteLine();
verseChunks = 0;
}
else
{
Console.Write(" ");
verseChunks += 1;
}
}
action += 1;
phrase = rand.Next(5);
phrasesAndVerses += 1;
if (! (verseChunks > 0 || IsEven(action)))
{
Console.Write(" ");
}
}
// verse loop
}
static void Main()
{
Console.Clear();
PrintCredits();
PressEnter("\nPress the Enter key to start. ");
Console.Clear();
Play();
}
}
Russian Roulette
// Russian Roulette
// Original version in BASIC:
// By Tom Adametx, 1978.
// Creative Computing's BASIC Games.
// - https://www.atariarchives.org/basicgames/showpage.php?page=141
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/russianroulette.bas
// - http://www.retroarchive.org/cpm/games/ccgames.zip
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-21.
//
// Last modified: 20251205T1547+0100.
using System;
class RussianRoulette
{
static void PressEnterToStart()
{
Console.Write("Press Enter to start. ");
Console.ReadKey();
}
static void PrintCredits()
{
Console.Clear();
Console.WriteLine("Russian Roulette\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
PressEnterToStart();
}
static void PrintInstructions()
{
Console.Clear();
Console.WriteLine("Here is a revolver.");
Console.WriteLine("Type 'f' to spin chamber and pull trigger.");
Console.WriteLine("Type 'g' to give up, and play again.");
Console.WriteLine("Type 'q' to quit.\n");
}
static void Play()
{
Random rand = new Random();
int times;
while (true) { // game loop
PrintInstructions();
times = 0;
bool playing = true;
while (playing)
{
Console.Write("> ");
switch (Console.ReadLine())
{
case "f" : // fire
if (rand.Next(100) > 83)
{
Console.WriteLine("Bang! You're dead!");
Console.WriteLine("Condolences will be sent to your relatives.");
playing = false;
}
else
{
times += 1;
if (times > 10)
{
Console.WriteLine("You win!");
Console.WriteLine("Let someone else blow his brains out.");
playing = false;
}
else
{
Console.WriteLine("Click.");
}
}
break;
case "g" :; // give up
Console.WriteLine("Chicken!");
playing = false; // force exit
break;
case "q" :; // quit
return;
}
}; // play loop
PressEnterToStart();
}; // game loop
}
static void Main()
{
PrintCredits();
Play();
Console.WriteLine("Bye!");
}
}
Seance
// Seance
// Original version in BASIC:
// By Chris Oxlade, 1983.
// Creepy Computer Games (Usborne Publishing Ltd.)
// - https://archive.org/details/seance.qb64
// - https://github.com/chaosotter/basic-games
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-25.
//
// Last modified: 20251205T1547+0100.
using System;
using System.Threading;
class Seance
{
const int MAX_SCORE = 50;
const int MAX_MESSAGE_LENGTH = 6;
const int MIN_MESSAGE_LENGTH = 3;
const int BASE_CHARACTER_CODE = (int) '@';
const char PLANCHETTE = '*';
const int FIRST_LETTER_NUMBER = 1;
const int LAST_LETTER_NUMBER = 26;
const ConsoleColor BOARD_COLOR = ConsoleColor.Cyan;
const ConsoleColor DEFAULT_COLOR = ConsoleColor.White;
const ConsoleColor INPUT_COLOR = ConsoleColor.Green;
const ConsoleColor INSTRUCTIONS_COLOR = ConsoleColor.DarkYellow;
const ConsoleColor MISTAKE_EFFECT_COLOR = ConsoleColor.Red;
const ConsoleColor PLANCHETTE_COLOR = ConsoleColor.DarkYellow;
const ConsoleColor TITLE_COLOR = ConsoleColor.Red;
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 = 3; // seconds
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 BOARD_HEIGHT = 5; // characters displayed on the left and right borders
const int BOARD_PAD = 1; // blank characters separating the board from its left and right borders
const int BOARD_WIDTH = 8; // characters displayed on the top and bottom borders
const int BOARD_X = 29; // screen column
const int BOARD_Y = 5; // screen line
// Print the given prompt and wait until the user enters a string.
//
static string Input(string prompt = "")
{
Console.ForegroundColor = INPUT_COLOR;
Console.Write(prompt);
string result = Console.ReadLine();
Console.ForegroundColor = DEFAULT_COLOR;
return result;
}
// Print the given prompt and wait until the user presses Enter.
//
static void PressEnter(string prompt)
{
Input(prompt);
}
// Return the x coordinate to print the given text centered on the board.
//
static int boardCenteredX(string text)
{
return BOARD_X + (BOARD_ACTUAL_WIDTH - text.Length) / 2;
}
// Print the given text on the given row, centered on the board.
//
static void PrintBoardCentered(string text, int y)
{
Console.SetCursorPosition(boardCenteredX(text), y);
Console.WriteLine(text);
}
const string TITLE = "Seance";
// Print the title at the current cursor position.
//
static void PrintTitle()
{
Console.ForegroundColor = TITLE_COLOR;
Console.WriteLine(TITLE);
Console.ForegroundColor = DEFAULT_COLOR;
}
// Print the title on the given row, centered on the board.
//
static void PrintBoardCenteredTitle(int y)
{
Console.ForegroundColor = TITLE_COLOR;
PrintBoardCentered(TITLE, y);
Console.ForegroundColor = DEFAULT_COLOR;
}
static void PrintCredits()
{
PrintTitle();
Console.WriteLine("\nOriginal version in BASIC:");
Console.WriteLine(" Written by Chris Oxlade, 1983.");
Console.WriteLine(" https://archive.org/details/seance.qb64");
Console.WriteLine(" https://github.com/chaosotter/basic-games");
Console.WriteLine("\nThis version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
}
static void PrintInstructions()
{
PrintTitle();
Console.ForegroundColor = INSTRUCTIONS_COLOR;
Console.WriteLine("\nMessages from the spirits are coming through, letter by letter. They want you");
Console.WriteLine("to remember the letters and type them into the computer in the correct order.");
Console.WriteLine("If you make mistakes, they will be angry -- very angry...");
Console.WriteLine();
Console.WriteLine("Watch for stars on your screen -- they show the letters in the spirits'");
Console.WriteLine("messages.");
Console.ForegroundColor = DEFAULT_COLOR;
}
// Print the given letter at the given board coordinates.
//
static void PrintCharacter(int y, int x, char a)
{
Console.SetCursorPosition(x + BOARD_X, y + BOARD_Y);
Console.Write(a);
}
static void PrintBoard()
{
Console.ForegroundColor = BOARD_COLOR;
for (int i = 1; i <= BOARD_WIDTH; i++)
{
PrintCharacter(0, i + 1, (char) (BASE_CHARACTER_CODE + i)); // top border
PrintCharacter(BOARD_BOTTOM_Y, i + 1, (char) (BASE_CHARACTER_CODE + LAST_LETTER_NUMBER - BOARD_HEIGHT - i + 1)); // bottom border
}
for (int i = 1; i <= BOARD_HEIGHT; i++)
{
PrintCharacter(i , 0, (char) (BASE_CHARACTER_CODE + LAST_LETTER_NUMBER - i + 1)); // left border
PrintCharacter(i , 3 + BOARD_WIDTH, (char) (BASE_CHARACTER_CODE + BOARD_WIDTH + i)); // right border
}
Console.WriteLine();
Console.ForegroundColor = DEFAULT_COLOR;
}
// Return a random integer in the given inclusive range.
//
static int RandomInInclusiveRange(int min, int max)
{
Random rand = new Random();
return rand.Next(max - min + 1) + min;
}
// Print the given mistake effect, wait a configured number of seconds and
// finally erase it.
//
static void PrintMistakeEffect(string effect)
{
int x = boardCenteredX(effect);
Console.CursorVisible = false;
Console.SetCursorPosition(x, MESSAGES_Y);
Console.ForegroundColor = MISTAKE_EFFECT_COLOR;
Console.WriteLine(effect);
Thread.Sleep(1000 * MISTAKE_EFFECT_PAUSE); // milliseconds
Console.ForegroundColor = DEFAULT_COLOR;
Console.SetCursorPosition(x, MESSAGES_Y);
string spaces = new string(' ', effect.Length);
Console.Write(spaces);
Console.CursorVisible = true;
}
// Return a new message of the given length, after marking its letters on the
// board.
//
static string Message(int length)
{
int y;
int x;
string message = "";
Console.CursorVisible = false;
for (int i = 1; i <= length; i++)
{
int letterNumber = RandomInInclusiveRange(FIRST_LETTER_NUMBER, LAST_LETTER_NUMBER);
char letter = (char) (BASE_CHARACTER_CODE + letterNumber);
message = $"{message}{letter}";
if (letterNumber <= BOARD_WIDTH)
{
// top border
y = 1;
x = letterNumber + 1;
}
else if (letterNumber <= BOARD_WIDTH + BOARD_HEIGHT)
{
// right border
y = letterNumber - BOARD_WIDTH;
x = 2 + BOARD_WIDTH;
}
else if (letterNumber <= BOARD_WIDTH + BOARD_HEIGHT + BOARD_WIDTH)
{
// bottom border
y = BOARD_BOTTOM_Y - 1;
x = 2 + BOARD_WIDTH + BOARD_HEIGHT + BOARD_WIDTH - letterNumber;
}
else
{
// left border
y = 1 + LAST_LETTER_NUMBER - letterNumber;
x = 1;
}
Console.ForegroundColor = PLANCHETTE_COLOR;
PrintCharacter(y, x, PLANCHETTE);
Console.ForegroundColor = DEFAULT_COLOR;
Thread.Sleep(1000); // milliseconds
PrintCharacter(y, x, ' ');
}
Console.CursorVisible = true;
return message;
}
// Accept a string from the user, erase it from the screen and return it.
//
static string MessageUnderstood()
{
string prompt = "? ";
Console.SetCursorPosition(INPUT_X, INPUT_Y);
string message = Input(prompt).ToUpper();
string spaces = new string(' ', prompt.Length + message.Length);
Console.SetCursorPosition(INPUT_X, INPUT_Y);
Console.Write(spaces);
return message;
}
static void Play()
{
int score = 0;
int mistakes = 0;
PrintBoardCenteredTitle(1);
PrintBoard();
while (true)
{
int messageLength = RandomInInclusiveRange(MIN_MESSAGE_LENGTH, MAX_MESSAGE_LENGTH);
if (Message(messageLength) != MessageUnderstood())
{
mistakes++;
switch (mistakes)
{
case 1:
PrintMistakeEffect("The table begins to shake!");
break;
case 2:
PrintMistakeEffect("The light bulb shatters!");
break;
case 3:
PrintMistakeEffect("Oh, no! A pair of clammy hands grasps your neck!");
return;
}
}
else
{
score += messageLength;
if (score >= MAX_SCORE)
{
PrintBoardCentered("Whew! The spirits have gone!", MESSAGES_Y);
PrintBoardCentered("You live to face another day!", MESSAGES_Y + 1);
return;
}
}
}
}
static void Main()
{
Console.ForegroundColor = DEFAULT_COLOR;
Console.Clear();
PrintCredits();
PressEnter("\nPress the Enter key to read the instructions. ");
Console.Clear();
PrintInstructions();
PressEnter("\nPress the Enter key to start. ");
Console.Clear();
Play();
Console.WriteLine("\n");
}
}
Sine Wave
// Sine Wave
// Original version in BASIC:
// Anonymous, 1978.
// Creative Computing's BASIC Games.
// - https://www.atariarchives.org/basicgames/showpage.php?page=146
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/sinewave.bas
// - http://www.retroarchive.org/cpm/games/ccgames.zip
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-20.
//
// Last modified: 20251205T1548+0100.
using System;
class SineWave
{
static string[] word = {"", ""};
// Ask the user to enter two words and store them into the `word` array.
//
static void getWords()
{
string[] order = {"first", "second"};
int n = 0;
while (n < 2)
{
while (word[n] == "")
{
Console.Write($"Enter the {order[n]} word: ");
word[n] = Console.ReadLine();
}
n++;
}
}
static void printCredits()
{
Console.WriteLine("Sine Wave\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Creative Computing (Morristown, New Jersey, USA), ca. 1980.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
Console.Write("Press Enter to start. ");
Console.ReadKey();
}
static void draw()
{
bool even = false;
double angle = 0.0;
while (angle <= 40.0)
{
int i = 0;
while (i < (int) (26 + 25 * Math.Sin(angle)))
{
Console.Write(" ");
i++;
}
Console.WriteLine(word[even ? 1 : 0]);
even = ! even;
angle += 0.25;
}
}
static void Main()
{
Console.Clear();
printCredits();
Console.Clear();
getWords();
Console.Clear();
draw();
}
}
Slots
// Slots
// A slot machine simulation.
// Original version in BASIC:
// By Fred Mirabelle and Bob Harper, 1973-01-29.
// Creative Computing's BASIC Games.
// - https://www.atariarchives.org/basicgames/showpage.php?page=149
// - http://vintage-basic.net/games.html
// - http://vintage-basic.net/bcg/slots.bas
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-22/24.
//
// Last modified: 20251205T1549+0100.
using System;
using System.Diagnostics;
class Slots
{
static string[] image = {" BAR ", " BELL ", "ORANGE", "LEMON ", " PLUM ", "CHERRY"};
const int BAR = 0; // position of "BAR" in `image`.
static ConsoleColor[] color =
{
ConsoleColor.White,
ConsoleColor.DarkCyan,
ConsoleColor.DarkYellow,
ConsoleColor.Yellow,
ConsoleColor.White,
ConsoleColor.Red };
const int MAX_BET = 100;
const int MIN_BET = 1;
static void PressEnter(string prompt)
{
Console.Write(prompt);
Console.ReadLine();
}
static void PrintCredits()
{
Console.Clear();
Console.WriteLine("Slots");
Console.WriteLine("A slot machine simulation.\n");
Console.WriteLine("Original version in BASIC:");
Console.WriteLine(" Creative computing (Morristown, New Jersey, USA).");
Console.WriteLine(" Produced by Fred Mirabelle and Bob Harper on 1973-01-29.\n");
Console.WriteLine("This version in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair\n");
PressEnter("Press Enter for instructions.");
}
static void PrintInstructions()
{
Console.Clear();
Console.WriteLine("You are in the H&M casino, in front of one of our");
Console.WriteLine($"one-arm bandits. Bet from {MIN_BET} to {MAX_BET} USD (or 0 to quit).\n");
PressEnter("Press Enter to start.");
}
static int Won(int prize, int bet)
{
switch (prize)
{
case 2: Console.WriteLine("DOUBLE!"); break;
case 5: Console.WriteLine("*DOUBLE BAR*"); break;
case 10: Console.WriteLine("**TOP DOLLAR**"); break;
case 100: Console.WriteLine("***JACKPOT***"); break;
}
Console.WriteLine("You won!");
return (prize + 1) * bet;
}
static void ShowStandings(int usd)
{
Console.WriteLine($"Your standings are {usd} USD.");
}
static void PrintReels(int[] reel)
{
ConsoleColor currentBackgroundColor = Console.BackgroundColor;
ConsoleColor currentForegroundColor = Console.ForegroundColor;
Console.SetCursorPosition(0, 0);
foreach (int r in reel)
{
Console.ForegroundColor = color[r];
Console.Write($"[{image[r]}] ");
}
Console.BackgroundColor = currentBackgroundColor;
Console.ForegroundColor = currentForegroundColor;
Console.WriteLine("");
}
static void InitReels(ref int[] reel)
{
Random rand = new Random();
int images = image.Length;
for (int i = 0; i < reel.Length; i++)
{
reel[i] = rand.Next(images);
}
}
static void SpinReels(ref int[] reel)
{
const int MILLISECONDS = 2000;
Console.CursorVisible = false;
Stopwatch timer = new Stopwatch();
timer.Start();
while(timer.ElapsedMilliseconds < MILLISECONDS)
{
InitReels(ref reel);
PrintReels(reel);
}
Console.CursorVisible = true;
}
// Return the number of equals and bars in the given array.
//
static (int, int) Prize(int[] reel)
{
int equals = 0;
int bars = 0;
for (int i = 0; i < reel.Length; i++)
{
for (int j = i + 1; j < reel.Length; j++)
{
equals += Convert.ToInt32(reel[i] == reel[j]);
}
}
if (equals > 0 && equals < reel.Length)
{
equals++;
}
foreach (int i in reel)
{
bars += Convert.ToInt32(i == BAR);
}
return (equals, bars);
}
static string GetString(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
static int GetInteger(string prompt)
{
int n = 0;
while (true)
{
try
{
n = Int32.Parse(GetString(prompt));
break;
}
catch { }
}
return n;
}
static void Play()
{
int standings = 0;
int bet = 0;
int[] reel = {0, 0, 0};
int equals = 0;
int bars = 0;
InitReels(ref reel);
while (true) { // play loop
while (true) { // bet loop
Console.Clear();
PrintReels(reel);
bet = GetInteger("Your bet (or 0 to quit): ");
if (bet > MAX_BET)
{
Console.WriteLine($"House limits are {MAX_BET} USD.");
PressEnter("Press Enter to try again.");
}
else if (bet < MIN_BET)
{
Console.WriteLine("Type \"q\" to confirm you want to quit.");
string confirm = Console.ReadLine().ToLower();
if (confirm == "q")
{
goto showStandings;
}
}
else
{
break; // bet loop
};
};
Console.Clear();
SpinReels(ref reel);
(equals, bars) = Prize(reel);
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:
Console.WriteLine("You lost.");
standings -= bet;
break;
};
ShowStandings(standings);
PressEnter("Press Enter to continue.");
}; // play loop
showStandings:
ShowStandings(standings);
if (standings < 0)
Console.WriteLine("Pay up! Please leave your money on the terminal.");
else if (standings == 0)
Console.WriteLine("Hey, you broke even.");
else if (standings > 0)
Console.WriteLine("Collect your winnings from the H&M cashier.");
}
static void Main()
{
PrintCredits();
PrintInstructions();
Play();
}
}
Stars
// Stars
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written on 2024-12-21.
//
// Last modified: 20251205T1549+0100.
using System;
using System.Linq;
class Stars
{
static void Main()
{
Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello, {name}.");
string answer;
string[] afirmativeAnswers = {"ok", "yeah", "yes", "y"};
do
{
double number;
while (true)
{
Console.Write("Enter a number: ");
try
{
number = Int64.Parse(Console.ReadLine());
break;
}
catch
{
Console.WriteLine("Integer expected.");
}
}
for (int i = 0; i < number; i++)
{
Console.Write("*");
}
Console.WriteLine();
Console.Write("Do you want more stars? ");
answer = Console.ReadLine().ToLower();
}
while (afirmativeAnswers.Contains(answer));
}
}
Strings
// Strings
// Original version in BASIC:
// Example included in Vintage BASIC 1.0.3.
// http://www.vintage-basic.net
// This version in C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-21/25.
//
// Last modified: 20251205T1549+0100.
using System;
class vintageBasicStrings
{
static string GetString(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
static int GetInteger(string prompt)
{
int n = 0;
while (true)
{
try
{
n = Int32.Parse(GetString(prompt));
break;
}
catch
{
}
}
return n;
}
static void Main()
{
string s = GetString("Enter a string: ");
int n = GetInteger("Enter an integer: ");
Console.WriteLine();
Console.Write($"ASC(\"{s}\") --> ");
Console.Write($"(int) \"{s}\"[0] --> ");
Console.WriteLine((int) s[0]);
Console.Write($"CHR$({n}) --> ");
Console.Write($"(char) ({n}) --> ");
Console.WriteLine("'" + (char) n + "'");
Console.Write($"LEFT$(\"{s}\", {n}) --> ");
Console.Write($"\"{s}\".Substring(0, Math.Min({n}, \"{s}\".Length) --> ");
Console.WriteLine("\"" + s.Substring(0, Math.Min(n, s.Length)) + "\"");
Console.Write($"MID$(\"{s}\", {n}) --> ");
Console.Write($"{n} > \"{s}\".Length ? \"\" : \"{s}\".Substring({n} - 1) --> ");
Console.WriteLine("\"" + (n > s.Length ? "" : s.Substring(n - 1)) + "\"");
Console.Write($"MID$(\"{s}\", {n}, 3) --> ");
Console.Write($"{n} > \"{s}\".Length ? \"\" : \"{s}\".Substring({n} - 1, Math.Min(3, \"{s}\".Length - {n} + 1)) --> ");
Console.WriteLine("\"" + (n > s.Length ? "" : s.Substring(n - 1, Math.Min(3, s.Length - n + 1)) + "\""));
Console.Write($"RIGHT$(\"{s}\", {n}) --> ");
Console.Write($"\"{s}\".Substring(Math.Max(0, \"{s}\".Length - {n}) --> ");
Console.WriteLine("\"" + s.Substring(Math.Max(0, s.Length - n)) + "\"");
Console.Write($"LEN(\"{s}\") --> ");
Console.Write($"\"{s}\".Length --> ");
Console.WriteLine(s.Length);
Console.Write($"VAL(\"{s}\") --> ");
int v;
try{ v = Int32.Parse(s); } catch { v = 0; };
Console.WriteLine($"int v; try{{ v = Int32.Parse(\"{s}\"); }} catch {{ v = 0; }} --> {v}");
Console.Write($"STR$({n}) --> ");
Console.Write($"Convert.ToString({n}) --> ");
Console.WriteLine("\"" + Convert.ToString(n) + "\"");
Console.Write($"SPC({n}) --> ");
Console.Write($"new string(' ', {n}) --> ");
Console.WriteLine("\"" + new string(' ', n) + "\"");
}
}
Xchange
// Xchange
// Original version in BASIC:
// 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 C#:
// Copyright (c) 2024, Marcos Cruz (programandala.net)
// SPDX-License-Identifier: Fair
//
// Written in 2024-12-29/2025-01-02.
//
// Last modified: 20251205T1550+0100.
using System;
using System.Linq;
class xchange
{
// Globals {{{1
// =============================================================
const ConsoleColor BOARD_INK = ConsoleColor.Cyan;
const ConsoleColor DEFAULT_INK = ConsoleColor.White;
const ConsoleColor INPUT_INK = ConsoleColor.Green;
const ConsoleColor INSTRUCTIONS_INK = ConsoleColor.DarkYellow;
const ConsoleColor TITLE_INK = ConsoleColor.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;
static string[] pristineGrid = new string[CELLS];
const int GRIDS_ROW = 3; // screen row where the grids are printed
const int GRIDS_COLUMN = 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 MAX_PLAYERS = 4;
static string[][] grid = new string[MAX_PLAYERS][];
static bool[] isPlaying = new bool[MAX_PLAYERS];
static int players;
const string QUIT_COMMAND = "X";
// User input {{{1
// =============================================================
// Print the given prompt and wait until the user enters an integer.
//
static int InputInt(string prompt = "")
{
Console.ForegroundColor = INPUT_INK;
int number;
while (true)
{
Console.Write(prompt);
try
{
number = (int) Int64.Parse(Console.ReadLine());
break;
}
catch
{
Console.WriteLine("Integer expected.");
}
}
Console.ForegroundColor = DEFAULT_INK;
return number;
}
// Print the given prompt and wait until the user enters a string.
//
static string InputString(string prompt = "")
{
Console.ForegroundColor = INPUT_INK;
Console.Write(prompt);
string result = Console.ReadLine();
Console.ForegroundColor = DEFAULT_INK;
return result;
}
// Print the given prompt and wait until the user presses Enter.
//
static void PressEnter(string prompt)
{
InputString(prompt);
}
// Return `true` if the given string is "yes" or a synonym.
//
static bool IsYes(string s)
{
string[] validOptions = {"ok", "y", "yeah", "yes"};
return validOptions.Contains(s.ToLower());
}
// Return `true` if the given string is "no" or a synonym.
//
static bool IsNo(string s)
{
string[] validOptions = {"n", "no", "nope"};
return validOptions.Contains(s);
}
// Print the given prompt, wait until the user enters a valid yes/no string,
// and return `true` for "yes" or `false` for "no".
//
static bool Yes(string prompt)
{
while (true)
{
string answer = InputString(prompt);
if (IsYes(answer))
{
return true;
}
if (IsNo(answer))
{
return false;
}
}
}
// Title, instructions and credits {{{1
// =============================================================
// Print the title at the current cursor position.
//
static void PrintTitle()
{
Console.ForegroundColor = TITLE_INK;
Console.WriteLine("Xchange");
Console.ForegroundColor = DEFAULT_INK;
}
// Print the credits at the current cursor position.
//
static void PrintCredits()
{
PrintTitle();
Console.WriteLine("\nOriginal version in BASIC:");
Console.WriteLine(" Written by Thomas C. McIntire, 1979.");
Console.WriteLine(" Published in \"The A to Z Book of Computer Games\", 1979.");
Console.WriteLine(" https://archive.org/details/The_A_to_Z_Book_of_Computer_Games/page/n269/mode/2up");
Console.WriteLine(" https://github.com/chaosotter/basic-games");
Console.WriteLine("\nThis improved remake in C#:");
Console.WriteLine(" Copyright (c) 2024, Marcos Cruz (programandala.net)");
Console.WriteLine(" SPDX-License-Identifier: Fair");
}
// Print the instructions at the current cursor position.
//
static void PrintInstructions()
{
PrintTitle();
Console.ForegroundColor = INSTRUCTIONS_INK;
Console.WriteLine("\nOne or two may play. If two, you take turns. A grid looks like this:\n");
Console.ForegroundColor = BOARD_INK;
Console.WriteLine(" F G D");
Console.Write($" A H {BLANK}\n");
Console.WriteLine(" E B C\n");
Console.ForegroundColor = INSTRUCTIONS_INK;
Console.WriteLine("But it should look like this:\n");
Console.ForegroundColor = BOARD_INK;
Console.WriteLine(" A B C");
Console.WriteLine(" D E F");
Console.Write($" G H {BLANK}\n\n");
Console.ForegroundColor = INSTRUCTIONS_INK;
Console.Write($"You may exchange any one letter with the '{BLANK}', but only one that's adjacent:\n");
Console.WriteLine("above, below, left, or right. Not all puzzles are possible, and you may enter");
Console.Write($"'{QUIT_COMMAND}' to give up.\n\n");
Console.WriteLine("Here we go...");
Console.ForegroundColor = DEFAULT_INK;
}
// Grids {{{1
// =============================================================
// Print the given player's grid title.
//
static void PrintGridTitle(int player)
{
Console.SetCursorPosition(GRIDS_COLUMN + (player * GRIDS_GAP), GRIDS_ROW);
Console.Write($"Player {player + 1}");
}
// Return the cursor position of the given player's grid cell.
//
static (int row, int column) CellPosition(int player, int cell)
{
int gridRow = cell / GRID_HEIGHT;
int gridColumn = cell % GRID_WIDTH;
int titleMargin = players > 1 ? 2 : 0;
return (GRIDS_ROW + titleMargin + gridRow, GRIDS_COLUMN + (gridColumn * CELLS_GAP) + (player * GRIDS_GAP));
}
// Return the cursor position of the given player's grid prompt.
//
static (int row, int column) GridPromptPosition(int player)
{
int gridRow = CELLS / GRID_HEIGHT;
int gridColumn = CELLS % GRID_WIDTH;
int titleMargin = players > 1 ? 2 : 0;
return (GRIDS_ROW + titleMargin + gridRow + 1, GRIDS_COLUMN + (gridColumn * CELLS_GAP) + (player * GRIDS_GAP));
}
// Print the given player's grid, in the given or default color.
//
static void PrintGrid(int player, ConsoleColor color = BOARD_INK)
{
if (players > 1)
{
PrintGridTitle(player);
}
Console.ForegroundColor = color;
for (int cell = 0; cell < CELLS; cell++)
{
(int y, int x) = CellPosition(player, cell);
Console.SetCursorPosition(x, y);
Console.Write(grid[player][cell]);
}
Console.ForegroundColor = DEFAULT_INK;
}
// Print the current players' grids.
//
static void PrintGrids()
{
for (int player = 0; player < players; player++)
{
if (isPlaying[player])
{
PrintGrid(player);
}
}
Console.WriteLine();
// EraseScreenDown(); // XXX TODO
}
// Scramble the grid of the given player.
//
static void ScrambleGrid(int player)
{
Random rand = new Random();
for (int cell = 0; cell < CELLS; cell++)
{
int randomCell = rand.Next(CELLS);
// Exchange the contents of the current cell with that of the random one.
string tmp = grid[player][cell];
grid[player][cell] = grid[player][randomCell];
grid[player][randomCell] = tmp;
}
}
// Init the grids.
//
static void InitGrids()
{
grid[0] = (string[]) pristineGrid.Clone();
ScrambleGrid(0);
for (int player = 0 + 1; player < players; player++)
{
grid[player] = (string[]) grid[0].Clone();
}
}
// Messages {{{1
// =============================================================
// Return a message prefix for the given player.
//
static string PlayerPrefix(int player)
{
return players > 1 ? $"Player {player + 1}: " : "";
}
// Return the cursor position of the given player's messages, adding the given
// row increment, which defaults to zero.
//
static (int row, int column) MessagePosition(int player, int rowInc = 0)
{
(int promptRow, _) = GridPromptPosition(player);
return (promptRow + 2 + rowInc, 1);
}
// Erase the current line to the right from the position of the cursor.
//
static void EraseLineRight()
{
int y = Console.CursorTop;
int x = Console.CursorLeft;
string blank = new String(' ', Console.WindowWidth - x - 1);
Console.Write(blank);
Console.SetCursorPosition(x, y);
}
// Print the given message about the given player, adding the given row
// increment, which defaults to zero, to the default cursor coordinates.
//
static void PrintMessage(string message, int player, int rowInc = 0)
{
(int y, int x) = MessagePosition(player, rowInc);
Console.SetCursorPosition(x, y);
Console.Write($"{PlayerPrefix(player)}{message}");
EraseLineRight();
Console.WriteLine();
}
// Erase the last message about the given player.
//
static void EraseMessage(int player)
{
(int y, int x) = MessagePosition(player);
Console.SetCursorPosition(x, y);
EraseLineRight();
}
// Game loop {{{1
// =============================================================
// Return a message with the players range.
//
static string PlayersRangeMessage()
{
return (MAX_PLAYERS == 2) ? "1 or 2" : $"from 1 to {MAX_PLAYERS}";
}
// Return the number of players, asking the user if needed.
//
static int NumberOfPlayers()
{
int players = 0;
PrintTitle();
Console.WriteLine();
while (players < 1 || players > MAX_PLAYERS)
{
players = InputInt($"Number of players ({PlayersRangeMessage()}): ");
}
return players;
}
// Is the given cell the first one on a grid row?
//
static bool isFirstCellOfA_gridRow(int cell)
{
return cell % GRID_WIDTH == 0;
}
// Is the given cell the last one on a grid row?
//
static bool isLastCellOfA_gridRow(int cell)
{
return (cell + 1) % GRID_WIDTH == 0;
}
// Are the given cells adjacent?
//
static bool AreCellsAdjacent(int cell_1, int cell_2)
{
if (cell_2 == cell_1 + 1 && ! isFirstCellOfA_gridRow(cell_2))
return true;
else if (cell_2 == cell_1 + GRID_WIDTH)
return true;
else if (cell_2 == cell_1 - 1 && ! isLastCellOfA_gridRow(cell_2))
return true;
else if (cell_2 == cell_1 - GRID_WIDTH)
return true;
else
return false;
}
// Is the given player's character cell a valid move, i.e. is it adjacent to
// the blank cell? If so, return the blank cell of the grid and `true`
// otherwise return a fake cell and `false`.
//
static (int blankCell, bool ok) IsLegalMove(int player, int charCell)
{
const int NOWHERE = -1;
for (int cell = 0; cell < CELLS; cell++)
{
if (grid[player][cell] == BLANK)
{
if (AreCellsAdjacent(charCell, cell))
{
return (cell, true);
}
break;
}
}
PrintMessage($"Illegal move \"{grid[player][charCell]}\".", player);
return (NOWHERE, false);
}
// Is the given player's command valid, i.e. a grid character? If so, return
// its position in the grid and `true`; otherwise print an error message and
// return a fake position and `false`.
//
static (int position, bool ok) IsValidCommand(int player, string command)
{
// XXX FIXME
const int NOWHERE = -1;
if (command != BLANK)
{
for (int i = 0; i < CELLS; i++)
{
if (grid[player][i] == command)
{
return (i, true);
}
}
}
PrintMessage($"Invalid character \"{command}\".", player);
return (NOWHERE, false);
}
// Forget the given player, who quitted.
//
static void ForgetPlayer(int player)
{
isPlaying[player] = false;
PrintGrid(player, DEFAULT_INK);
}
// Play the turn of the given player.
//
static void PlayTurn(int player)
{
int blankPosition;
int characterPosition = 0;
if (isPlaying[player])
{
while (true)
{
string command;
bool ok = false;
while (! ok)
{
(int y, int x) = GridPromptPosition(player);
Console.SetCursorPosition(x, y);
EraseLineRight();
Console.SetCursorPosition(x, y);
command = InputString("Move: ").Trim().ToUpper();
if (command == QUIT_COMMAND)
{
ForgetPlayer(player);
return;
}
(characterPosition, ok) = IsValidCommand(player, command);
}
(blankPosition, ok) = IsLegalMove(player, characterPosition);
if (ok)
{
break;
}
}
EraseMessage(player);
grid[player][blankPosition] = grid[player][characterPosition];
grid[player][characterPosition] = BLANK;
}
}
// Play the turns of all players.
//
static void PlayTurns()
{
for (int player = 0; player < players; player++) PlayTurn(player);
}
// Is someone playing?
//
static bool IsSomeonePlaying()
{
for (int player = 0; player < players; player++)
{
if (isPlaying[player])
{
return true;
}
}
return false;
}
// Has someone won? If so, print a message for every winner and return `true`
// otherwise just return `false`.
//
static bool HasSomeoneWon()
{
int winners = 0;
for (int player = 0; player < players; player++)
{
if (isPlaying[player])
{
if (grid[player].SequenceEqual(pristineGrid))
{
winners += 1;
if (winners > 0)
{
string too = winners > 1 ? ", too" : "";
PrintMessage
(
$"You're the winner{too}!",
player,
winners - 1
);
}
}
}
}
return winners > 0;
}
// Init the game.
//
static void InitGame()
{
Console.Clear();
players = NumberOfPlayers();
for (int player = 0; player < players; player++)
{
isPlaying[player] = true;
}
Console.Clear();
PrintTitle();
InitGrids();
PrintGrids();
}
// Play the game.
//
static void Play()
{
InitGame();
while (IsSomeonePlaying())
{
PlayTurns();
PrintGrids();
if (HasSomeoneWon())
{
break;
}
}
}
// Main {{{1
// =============================================================
// Init the program, i.e. just once before the first game.
//
static void InitOnce()
{
// Init the pristine grid.
const int FIRST_CHAR_CODE = (int) 'A';
for (int i = 0; i < CELLS - 1; i++)
{
pristineGrid[i] = ((char) (FIRST_CHAR_CODE + i)).ToString();
}
pristineGrid[CELLS - 1] = BLANK;
// Init the grid
for (int i = 0; i < MAX_PLAYERS; i++)
{
grid[i] = new string[CELLS];
grid[i] = pristineGrid;
}
}
// Return `true` if the player does not want to play another game; otherwise
// return `false`.
//
static bool Enough()
{
(int y, int x) = (GridPromptPosition(player : 0));
Console.SetCursorPosition(x, y);
return ! Yes("Another game? ");
}
static void Main()
{
InitOnce();
Console.Clear();
PrintCredits();
PressEnter("\nPress the Enter key to read the instructions. ");
Console.Clear();
PrintInstructions();
PressEnter("\nPress the Enter key to start. ");
while (true)
{
Play();
if (Enough())
{
break;
}
}
Console.WriteLine("So long…");
}
}
