Merge branch 'coding-horror:main' into main

This commit is contained in:
Andrei Ursu
2022-08-28 04:46:21 +02:00
committed by GitHub
84 changed files with 1625 additions and 4 deletions

View File

@@ -1,9 +1,15 @@
#### Alternate Languages #### Alternate Languages
This folder contains implementations of each program in alternate languages which are _not_ one of the agreed upon 10 languages, intended to meet these three criteria: This folder contains implementations of each program in alternate languages which are _not_ one of the agreed upon 10 languages.
Implementations here are NOT bound to these three criteria:
1. Popular (by TIOBE index) 1. Popular (by TIOBE index)
2. Memory safe 2. Memory safe
3. Generally considered a 'scripting' language 3. Generally considered a 'scripting' language
We welcome additional ports in whatever language you prefer, but these additional ports are for educational purposes only, and do not count towards the donation total at the end of the project. So for example, (here only) C or PASCAL are allowed. Please still remain faithful to original look-and-feel (console applications).
Try to keep your code portable (unless it is not possible, and then be very explicit about this limitation in your
README and your folder naming).
We welcome additional ports in whatever language you prefer, but these additional ports are for educational purposes only, and do not count towards the donation total at the end of the project.

View File

@@ -16,3 +16,27 @@ http://www.vintage-basic.net/games.html
#### Porting Notes #### Porting Notes
(please note any difficulties or challenges in porting here) (please note any difficulties or challenges in porting here)
##### Randomization Logic
The BASIC code uses an interesting technique for choosing the random coordinates for the mines. The first coordinate is
chosen like this:
```basic
380 LET A=INT(3*(RND(X)))
390 IF A<>0 THEN 410
400 LET A=3
```
where line 410 is the start of a similar block of code for the next coordinate. The behaviour of `RND(X)` depends on the
value of `X`. If `X` is greater than zero then it returns a random value between 0 and 1. If `X` is zero it returns the
last random value generated, or 0 if no value has yet been generated.
If `X` is 1, therefore, the first line above set `A` to 0, 1, or 2. The next 2 lines replace a 0 with a 3. The
replacement values varies for the different coordinates with the result that the random selection is biased towards a
specific set of points. If `X` is 0, the `RND` calls all return 0, so the coordinates are the known. It appears that
this technique was probably used to allow testing the game with a well-known set of locations for the mines. However, in
the code as it comes to us, the value of `X` is never set and is thus 0, so the mine locations are never randomized.
The C# port implements the biased randomized mine locations, as seems to be the original intent, but includes a
command-line switch to enable the deterministic execution as well.

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

104
30_Cube/csharp/Game.cs Normal file
View File

@@ -0,0 +1,104 @@
namespace Cube;
internal class Game
{
private const int _initialBalance = 500;
private readonly IEnumerable<(int, int, int)> _seeds = new List<(int, int, int)>
{
(3, 2, 3), (1, 3, 3), (3, 3, 2), (3, 2, 3), (3, 1, 3)
};
private readonly (float, float, float) _startLocation = (1, 1, 1);
private readonly (float, float, float) _goalLocation = (3, 3, 3);
private readonly IReadWrite _io;
private readonly IRandom _random;
public Game(IReadWrite io, IRandom random)
{
_io = io;
_random = random;
}
public void Play()
{
_io.Write(Streams.Introduction);
if (_io.ReadNumber("") != 0)
{
_io.Write(Streams.Instructions);
}
PlaySeries(_initialBalance);
_io.Write(Streams.Goodbye);
}
private void PlaySeries(float balance)
{
while (true)
{
var wager = _io.ReadWager(balance);
var gameWon = PlayGame();
if (wager.HasValue)
{
balance = gameWon ? (balance + wager.Value) : (balance - wager.Value);
if (balance <= 0)
{
_io.Write(Streams.Bust);
return;
}
_io.WriteLine(Formats.Balance, balance);
}
if (_io.ReadNumber(Prompts.TryAgain) != 1) { return; }
}
}
private bool PlayGame()
{
var mineLocations = _seeds.Select(seed => _random.NextLocation(seed)).ToHashSet();
var currentLocation = _startLocation;
var prompt = Prompts.YourMove;
while (true)
{
var newLocation = _io.Read3Numbers(prompt);
if (!MoveIsLegal(currentLocation, newLocation)) { return Lose(Streams.IllegalMove); }
currentLocation = newLocation;
if (currentLocation == _goalLocation) { return Win(Streams.Congratulations); }
if (mineLocations.Contains(currentLocation)) { return Lose(Streams.Bang); }
prompt = Prompts.NextMove;
}
}
private bool Lose(Stream text)
{
_io.Write(text);
return false;
}
private bool Win(Stream text)
{
_io.Write(text);
return true;
}
private bool MoveIsLegal((float, float, float) from, (float, float, float) to)
=> (to.Item1 - from.Item1, to.Item2 - from.Item2, to.Item3 - from.Item3) switch
{
( > 1, _, _) => false,
(_, > 1, _) => false,
(_, _, > 1) => false,
(1, 1, _) => false,
(1, _, 1) => false,
(_, 1, 1) => false,
_ => true
};
}

View File

@@ -0,0 +1,20 @@
namespace Cube;
internal static class IOExtensions
{
internal static float? ReadWager(this IReadWrite io, float balance)
{
io.Write(Streams.Wager);
if (io.ReadNumber("") == 0) { return null; }
var prompt = Prompts.HowMuch;
while(true)
{
var wager = io.ReadNumber(prompt);
if (wager <= balance) { return wager; }
prompt = Prompts.BetAgain;
}
}
}

10
30_Cube/csharp/Program.cs Normal file
View File

@@ -0,0 +1,10 @@
global using Games.Common.IO;
global using Games.Common.Randomness;
global using static Cube.Resources.Resource;
using Cube;
IRandom random = args.Contains("--non-random") ? new ZerosGenerator() : new RandomNumberGenerator();
new Game(new ConsoleIO(), random).Play();

View File

@@ -1,3 +1,12 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
#### Execution
As noted in the main Readme file, the randomization code in the BASIC program has a switch (the variable `X`) that
allows the game to be run in a deterministic (non-random) mode.
Running the C# port without command-line parameters will play the game with random mine locations.
Running the port with a `--non-random` command-line switch will run the game with non-random mine locations.

View File

@@ -0,0 +1,14 @@
namespace Cube;
internal static class RandomExtensions
{
internal static (float, float, float) NextLocation(this IRandom random, (int, int, int) bias)
=> (random.NextCoordinate(bias.Item1), random.NextCoordinate(bias.Item2), random.NextCoordinate(bias.Item3));
private static float NextCoordinate(this IRandom random, int bias)
{
var value = random.Next(3);
if (value == 0) { value = bias; }
return value;
}
}

View File

@@ -0,0 +1 @@
You now have {0} dollars.

View File

@@ -0,0 +1,4 @@
******BANG******
You lose!

View File

@@ -0,0 +1 @@
Tried to fool me; bet again

View File

@@ -0,0 +1 @@
You bust.

View File

@@ -0,0 +1 @@
Congratulations!

View File

@@ -0,0 +1,3 @@
Tough luck!
Goodbye.

View File

@@ -0,0 +1 @@
How much

View File

@@ -0,0 +1,2 @@
Illegal move. You lose.

View File

@@ -0,0 +1,24 @@
This is a game in which you will be playing against the
random decision od the computer. The field of play is a
cube of side 3. Any of the 27 locations can be designated
by inputing three numbers such as 2,3,1. At the start,
you are automatically at location 1,1,1. The object of
the game is to get to location 3,3,3. One minor detail:
the computer will pick, at random, 5 locations at which
it will play land mines. If you hit one of these locations
you lose. One other details: you may move only one space
in one direction each move. For example: from 1,1,2 you
may move to 2,1,2 or 1,1,3. You may not change
two of the numbers on the same move. If you make an illegal
move, you lose and the computer takes the money you may
have bet on that round.
All Yes or No questions will be answered by a 1 for Yes
or a 0 (zero) for no.
When stating the amount of a wager, print only the number
of dollars (example: 250) You are automatically started with
500 dollars in your account.
Good luck!

View File

@@ -0,0 +1,6 @@
Cube
Creative Computing Morristown, New Jersey
Do you want to see the instructions? (Yes--1,No--0)

View File

@@ -0,0 +1 @@
Next move:

View File

@@ -0,0 +1,44 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Cube.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
public static Stream Instructions => GetStream();
public static Stream Wager => GetStream();
public static Stream IllegalMove => GetStream();
public static Stream Bang => GetStream();
public static Stream Bust => GetStream();
public static Stream Congratulations => GetStream();
public static Stream Goodbye => GetStream();
}
internal static class Prompts
{
public static string HowMuch => GetString();
public static string BetAgain => GetString();
public static string YourMove => GetString();
public static string NextMove => GetString();
public static string TryAgain => GetString();
}
internal static class Formats
{
public static string Balance => GetString();
}
private static string GetString([CallerMemberName] string? name = null)
{
using var stream = GetStream(name);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1 @@
Do you want to try again

View File

@@ -0,0 +1 @@
Want to make a wager

View File

@@ -0,0 +1,2 @@
It's your move:

View File

@@ -0,0 +1,10 @@
namespace Cube;
internal class ZerosGenerator : IRandom
{
public float NextFloat() => 0;
public float PreviousFloat() => 0;
public void Reseed(int seed) { }
}

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,57 @@
using System.Text;
using static Diamond.Resources.Resource;
namespace Diamond;
internal class Pattern
{
private readonly IReadWrite _io;
public Pattern(IReadWrite io)
{
_io = io;
io.Write(Streams.Introduction);
}
public void Draw()
{
var diamondSize = _io.ReadNumber(Prompts.TypeNumber);
_io.WriteLine();
var diamondCount = (int)(60 / diamondSize);
var diamondLines = new List<string>(GetDiamondLines(diamondSize)).AsReadOnly();
for (int patternRow = 0; patternRow < diamondCount; patternRow++)
{
for (int diamondRow = 0; diamondRow < diamondLines.Count; diamondRow++)
{
var line = new StringBuilder();
for (int patternColumn = 0; patternColumn < diamondCount; patternColumn++)
{
line.PadToLength((int)(patternColumn * diamondSize)).Append(diamondLines[diamondRow]);
}
_io.WriteLine(line);
}
}
}
public static IEnumerable<string> GetDiamondLines(float size)
{
for (var i = 1; i <= size; i += 2)
{
yield return GetLine(i);
}
for (var i = size - 2; i >= 1; i -= 2)
{
yield return GetLine(i);
}
string GetLine(float i) =>
string.Concat(
new string(' ', (int)(size - i) / 2),
new string('C', Math.Min((int)i, 2)),
new string('!', Math.Max(0, (int)i - 2)));
}
}

View File

@@ -0,0 +1,4 @@
global using Games.Common.IO;
using Diamond;
new Pattern(new ConsoleIO()).Draw();

View File

@@ -0,0 +1,5 @@
Diamond
Creative Computing Morristown, New Jersey

View File

@@ -0,0 +1,29 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Diamond.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
}
internal static class Prompts
{
public static string TypeNumber => GetString();
}
private static string GetString([CallerMemberName] string? name = null)
{
using var stream = GetStream(name);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1,22 @@
Chomp is for 1 or more players (humans only).
Here's how a board looks (this one is 5 by 7):
1 2 3 4 5 6 7 8 9
1 P * * * * * *
2 * * * * * * *
3 * * * * * * *
4 * * * * * * *
5 * * * * * * *
The board is a big cookie - R rows high and C columns
wide. You input R and C at the start. In the upper left
corner of the cookie is a poison square (P). The one who
chomps the poison square loses. To take a chomp, type the
row and column of one of the squares on the cookie.
All of the squares below and to the right of that square
(including that square, too) disappear -- Chomp!!
No fair chomping on squares that have already been chomped,
or that are outside the original dimensions of the cookie.

View File

@@ -0,0 +1,2 @@
For a pretty diamond pattern,
type in an odd number between 5 and 21

View File

@@ -0,0 +1,9 @@
using System.Text;
namespace Diamond;
internal static class StringBuilderExtensions
{
internal static StringBuilder PadToLength(this StringBuilder builder, int length) =>
builder.Append(' ', length - builder.Length);
}

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

80
34_Digits/csharp/Game.cs Normal file
View File

@@ -0,0 +1,80 @@
namespace Digits;
internal class GameSeries
{
private readonly IReadOnlyList<int> _weights = new List<int> { 0, 1, 3 }.AsReadOnly();
private readonly IReadWrite _io;
private readonly IRandom _random;
public GameSeries(IReadWrite io, IRandom random)
{
_io = io;
_random = random;
}
internal void Play()
{
_io.Write(Streams.Introduction);
if (_io.ReadNumber(Prompts.ForInstructions) != 0)
{
_io.Write(Streams.Instructions);
}
do
{
new Game(_io, _random).Play();
} while (_io.ReadNumber(Prompts.WantToTryAgain) == 1);
_io.Write(Streams.Thanks);
}
}
internal class Game
{
private readonly IReadWrite _io;
private readonly Guesser _guesser;
public Game(IReadWrite io, IRandom random)
{
_io = io;
_guesser = new Guesser(random);
}
public void Play()
{
var correctGuesses = 0;
for (int round = 0; round < 3; round++)
{
var digits = _io.Read10Digits(Prompts.TenNumbers, Streams.TryAgain);
correctGuesses = GuessDigits(digits, correctGuesses);
}
_io.Write(correctGuesses switch
{
< 10 => Streams.YouWin,
10 => Streams.ItsATie,
> 10 => Streams.IWin
});
}
private int GuessDigits(IEnumerable<int> digits, int correctGuesses)
{
_io.Write(Streams.Headings);
foreach (var digit in digits)
{
var guess = _guesser.GuessNextDigit();
if (guess == digit) { correctGuesses++; }
_io.WriteLine(Formats.GuessResult, guess, digit, guess == digit ? "Right" : "Wrong", correctGuesses);
_guesser.ObserveActualDigit(digit);
}
return correctGuesses;
}
}

View File

@@ -0,0 +1,32 @@
namespace Digits;
internal class Guesser
{
private readonly Memory _matrices = new();
private readonly IRandom _random;
public Guesser(IRandom random)
{
_random = random;
}
public int GuessNextDigit()
{
var currentSum = 0;
var guess = 0;
for (int i = 0; i < 3; i++)
{
var sum = _matrices.GetWeightedSum(i);
if (sum > currentSum || _random.NextFloat() >= 0.5)
{
currentSum = sum;
guess = i;
}
}
return guess;
}
public void ObserveActualDigit(int digit) => _matrices.ObserveDigit(digit);
}

View File

@@ -0,0 +1,20 @@
namespace Digits;
internal static class IOExtensions
{
internal static IEnumerable<int> Read10Digits(this IReadWrite io, string prompt, Stream retryText)
{
while (true)
{
var numbers = new float[10];
io.ReadNumbers(prompt, numbers);
if (numbers.All(n => n == 0 || n == 1 || n == 2))
{
return numbers.Select(n => (int)n);
}
io.Write(retryText);
}
}
}

View File

@@ -0,0 +1,27 @@
namespace Digits;
internal class Matrix
{
private readonly int _weight;
private readonly int[,] _values;
public Matrix(int width, int weight, Func<int, int, int> seedFactory)
{
_weight = weight;
_values = new int[width, 3];
for (int i = 0; i < width; i++)
for (int j = 0; j < 3; j++)
{
_values[i, j] = seedFactory.Invoke(i, j);
}
Index = width - 1;
}
public int Index { get; set; }
public int GetWeightedValue(int row) => _weight * _values[Index, row];
public int IncrementValue(int row) => _values[Index, row]++;
}

View File

@@ -0,0 +1,30 @@
namespace Digits;
public class Memory
{
private readonly Matrix[] _matrices;
public Memory()
{
_matrices = new[]
{
new Matrix(27, 3, (_, _) => 1),
new Matrix(9, 1, (i, j) => i == 4 * j ? 2 : 3),
new Matrix(3, 0, (_, _) => 9)
};
}
public int GetWeightedSum(int row) => _matrices.Select(m => m.GetWeightedValue(row)).Sum();
public void ObserveDigit(int digit)
{
for (int i = 0; i < 3; i++)
{
_matrices[i].IncrementValue(digit);
}
_matrices[0].Index = _matrices[0].Index % 9 * 3 + digit;
_matrices[1].Index = _matrices[0].Index % 9;
_matrices[2].Index = digit;
}
}

View File

@@ -0,0 +1,6 @@
global using Digits;
global using Games.Common.IO;
global using Games.Common.Randomness;
global using static Digits.Resources.Resource;
new GameSeries(new ConsoleIO(), new RandomNumberGenerator()).Play();

View File

@@ -0,0 +1 @@
For instructions, type '1', else type '0'

View File

@@ -0,0 +1 @@
{0} {1} {2} {3}

View File

@@ -0,0 +1,3 @@
My guess Your no. Result No. right

View File

@@ -0,0 +1,4 @@
I guessed more than 1/3 of your numbers.
I win.

View File

@@ -0,0 +1,11 @@
Please take a piece of paper and write down
the digits '0', '1', or '2' thirty times at random.
Arrange them in three lines of ten digits each.
I will ask for then ten at a time.
I will always guess them first and then look at your
next number to see if I was right. By pure luck,
I ought to be right ten times. But I hope to do better
than that *****

View File

@@ -0,0 +1,6 @@
Digits
Creative Computing Morristown, New Jersey
This is a game of guessing.

View File

@@ -0,0 +1,4 @@
I guessed exactly 1/3 of your numbers.
It's a tie game.

View File

@@ -0,0 +1,43 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Digits.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
public static Stream Instructions => GetStream();
public static Stream TryAgain => GetStream();
public static Stream ItsATie => GetStream();
public static Stream IWin => GetStream();
public static Stream YouWin => GetStream();
public static Stream Thanks => GetStream();
public static Stream Headings => GetStream();
}
internal static class Prompts
{
public static string ForInstructions => GetString();
public static string TenNumbers => GetString();
public static string WantToTryAgain => GetString();
}
internal static class Formats
{
public static string GuessResult => GetString();
}
private static string GetString([CallerMemberName] string? name = null)
{
using var stream = GetStream(name);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1,2 @@
Ten numbers, please

View File

@@ -0,0 +1,2 @@
Thanks for the game

View File

@@ -0,0 +1,2 @@
Only use the digits '0', '1', or '2'.
Let's try again.

View File

@@ -0,0 +1 @@
Do you want to try again (1 for yes, 0 for no)

View File

@@ -0,0 +1,4 @@
I guessed less than 1/3 of your numbers.
You beat me. Congratulations *****

78
41_Guess/csharp/Game.cs Normal file
View File

@@ -0,0 +1,78 @@
namespace Guess;
internal class Game
{
private readonly IReadWrite _io;
private readonly IRandom _random;
public Game(IReadWrite io, IRandom random)
{
_io = io;
_random = random;
}
public void Play()
{
while (true)
{
_io.Write(Streams.Introduction);
var limit = _io.ReadNumber(Prompts.Limit);
_io.WriteLine();
// There's a bug here that exists in the original code.
// If the limit entered is <= 0 then the program will crash.
var targetGuessCount = checked((int)Math.Log2(limit) + 1);
PlayGuessingRounds(limit, targetGuessCount);
_io.Write(Streams.BlankLines);
}
}
private void PlayGuessingRounds(float limit, int targetGuessCount)
{
while (true)
{
_io.WriteLine(Formats.Thinking, limit);
// There's a bug here that exists in the original code. If a non-integer is entered as the limit
// then it's possible for the secret number to be the next integer greater than the limit.
var secretNumber = (int)_random.NextFloat(limit) + 1;
var guessCount = 0;
while (true)
{
var guess = _io.ReadNumber("");
if (guess <= 0) { return; }
guessCount++;
if (IsGuessCorrect(guess, secretNumber)) { break; }
}
ReportResult(guessCount, targetGuessCount);
_io.Write(Streams.BlankLines);
}
}
private bool IsGuessCorrect(float guess, int secretNumber)
{
if (guess < secretNumber) { _io.Write(Streams.TooLow); }
if (guess > secretNumber) { _io.Write(Streams.TooHigh); }
return guess == secretNumber;
}
private void ReportResult(int guessCount, int targetGuessCount)
{
_io.WriteLine(Formats.ThatsIt, guessCount);
_io.WriteLine(
(guessCount - targetGuessCount) switch
{
< 0 => Strings.VeryGood,
0 => Strings.Good,
> 0 => string.Format(Formats.ShouldHave, targetGuessCount)
});
}
}

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,7 @@
global using Games.Common.IO;
global using Games.Common.Randomness;
global using static Guess.Resources.Resource;
using Guess;
new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();

View File

@@ -0,0 +1,5 @@

View File

@@ -0,0 +1 @@
Good.

View File

@@ -0,0 +1,9 @@
Guess
Creative Computing Morristown, New Jersey
This is a number guessing game. I'll think
of a number between 1 and any limit you want.
The you have to guess what it is.

View File

@@ -0,0 +1 @@
What limit do you want

View File

@@ -0,0 +1,44 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Guess.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
public static Stream TooLow => GetStream();
public static Stream TooHigh => GetStream();
public static Stream BlankLines => GetStream();
}
internal static class Formats
{
public static string Thinking => GetString();
public static string ThatsIt => GetString();
public static string ShouldHave => GetString();
}
internal static class Prompts
{
public static string Limit => GetString();
}
internal static class Strings
{
public static string Good => GetString();
public static string VeryGood => GetString();
}
private static string GetString([CallerMemberName] string? name = null)
{
using var stream = GetStream(name);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1 @@
You should have been able to get it in only {0}

View File

@@ -0,0 +1 @@
That's it! You got it in {0} tries.

View File

@@ -0,0 +1,2 @@
I'm thinking of a number between 1 and {0}
Now you try to guess what it is.

View File

@@ -0,0 +1 @@
Too high. Try a smaller answer.

View File

@@ -0,0 +1 @@
Too low. Try a bigger answer.

View File

@@ -0,0 +1 @@
Very good.

View File

@@ -6,6 +6,7 @@ There are two players; the game is played on a 5x5 board and each player has a s
The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example: The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example:
```
| | 1 | 2 | 3 | 4 | 5 | | | 1 | 2 | 3 | 4 | 5 |
|:-:|:-:|:-:|:-:|:-:|:-:| |:-:|:-:|:-:|:-:|:-:|:-:|
| 1 | | | | | | | 1 | | | | | |
@@ -13,9 +14,10 @@ The # and * are regarded as the same except when deciding whether to generate a
| 3 | | | | # | | | 3 | | | | # | |
| 4 | | | # | | | | 4 | | | # | | |
| 5 | | | | | | | 5 | | | | | |
```
A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become: A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become:
```
| | 1 | 2 | 3 | 4 | 5 | | | 1 | 2 | 3 | 4 | 5 |
|:-:|:-:|:-:|:-:|:-:|:-:| |:-:|:-:|:-:|:-:|:-:|:-:|
| 1 | | | | | | | 1 | | | | | |
@@ -23,7 +25,7 @@ A new cell will be generated at (3,3) which will be a `#` since there are two `#
| 3 | | | # | # | | | 3 | | | # | # | |
| 4 | | | | | | | 4 | | | | | |
| 5 | | | | | | | 5 | | | | | |
```
On the first most each player positions 3 pieces of life on the board by typing in the co-ordinates of the pieces. (In the event of the same cell being chosen by both players that cell is left empty.) On the first most each player positions 3 pieces of life on the board by typing in the co-ordinates of the pieces. (In the event of the same cell being chosen by both players that cell is left empty.)
The board is then adjusted to the next generation and printed out. The board is then adjusted to the next generation and printed out.

View File

@@ -0,0 +1,305 @@
import java.util.*;
import java.util.stream.IntStream;
/**
* Life for Two
* <p>
* The original BASIC program uses a grid with an extras border of cells all around,
* probably to simplify calculations and manipulations. This java program has the exact
* grid size and instead uses boundary check conditions in the logic.
* <p>
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
*/
public class LifeForTwo {
final static int GRID_SIZE = 5;
//Pair of offset which when added to the current cell's coordinates,
// give the coordinates of the neighbours
final static int[] neighbourCellOffsets = {
-1, 0,
1, 0,
0, -1,
0, 1,
-1, -1,
1, -1,
-1, 1,
1, 1
};
//The best term that I could come with to describe these numbers was 'masks'
//They act like indicators to decide which player won the cell. The value is the score of the cell after all the
// generation calculations.
final static List<Integer> maskPlayer1 = List.of(3, 102, 103, 120, 130, 121, 112, 111, 12);
final static List<Integer> maskPlayer2 = List.of(21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012);
public static void main(String[] args) {
printIntro();
Scanner scan = new Scanner(System.in);
scan.useDelimiter("\\D");
int[][] grid = new int[GRID_SIZE][GRID_SIZE];
initializeGrid(grid);
//Read the initial 3 moves for each player
for (int b = 1; b <= 2; b++) {
System.out.printf("\nPLAYER %d - 3 LIVE PIECES.%n", b);
for (int k1 = 1; k1 <= 3; k1++) {
var player1Coordinates = readUntilValidCoordinates(scan, grid);
grid[player1Coordinates.x - 1][player1Coordinates.y - 1] = (b == 1 ? 3 : 30);
}
}
printGrid(grid);
calculatePlayersScore(grid); //Convert 3, 30 to 100, 1000
resetGridForNextGen(grid);
computeCellScoresForOneGen(grid);
var playerScores = calculatePlayersScore(grid);
resetGridForNextGen(grid);
boolean gameOver = false;
while (!gameOver) {
printGrid(grid);
if (playerScores.getPlayer1Score() == 0 && playerScores.getPlayer2Score() == 0) {
System.out.println("\nA DRAW");
gameOver = true;
} else if (playerScores.getPlayer2Score() == 0) {
System.out.println("\nPLAYER 1 IS THE WINNER");
gameOver = true;
} else if (playerScores.getPlayer1Score() == 0) {
System.out.println("\nPLAYER 2 IS THE WINNER");
gameOver = true;
} else {
System.out.print("PLAYER 1 ");
Coordinate player1Move = readCoordinate(scan);
System.out.print("PLAYER 2 ");
Coordinate player2Move = readCoordinate(scan);
if (!player1Move.equals(player2Move)) {
grid[player1Move.x - 1][player1Move.y - 1] = 100;
grid[player2Move.x - 1][player2Move.y - 1] = 1000;
}
//In the original, B is assigned 99 when both players choose the same cell
//and that is used to control the flow
computeCellScoresForOneGen(grid);
playerScores = calculatePlayersScore(grid);
resetGridForNextGen(grid);
}
}
}
private static void initializeGrid(int[][] grid) {
for (int[] row : grid) {
Arrays.fill(row, 0);
}
}
private static void computeCellScoresForOneGen(int[][] grid) {
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] >= 100) {
calculateScoreForOccupiedCell(grid, i, j);
}
}
}
}
private static Scores calculatePlayersScore(int[][] grid) {
int m2 = 0;
int m3 = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] < 3) {
grid[i][j] = 0;
} else {
if (maskPlayer1.contains(grid[i][j])) {
m2++;
} else if (maskPlayer2.contains(grid[i][j])) {
m3++;
}
}
}
}
return new Scores(m2, m3);
}
private static void resetGridForNextGen(int[][] grid) {
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] < 3) {
grid[i][j] = 0;
} else {
if (maskPlayer1.contains(grid[i][j])) {
grid[i][j] = 100;
} else if (maskPlayer2.contains(grid[i][j])) {
grid[i][j] = 1000;
} else {
grid[i][j] = 0;
}
}
}
}
}
private static void calculateScoreForOccupiedCell(int[][] grid, int i, int j) {
var b = 1;
if (grid[i][j] > 999) {
b = 10;
}
for (int k = 0; k < 15; k += 2) {
//check bounds
var neighbourX = i + neighbourCellOffsets[k];
var neighbourY = j + neighbourCellOffsets[k + 1];
if (neighbourX >= 0 && neighbourX < GRID_SIZE &&
neighbourY >= 0 && neighbourY < GRID_SIZE) {
grid[neighbourX][neighbourY] = grid[neighbourX][neighbourY] + b;
}
}
}
private static void printGrid(int[][] grid) {
System.out.println();
printRowEdge();
System.out.println();
for (int i = 0; i < grid.length; i++) {
System.out.printf("%d ", i + 1);
for (int j = 0; j < grid[i].length; j++) {
System.out.printf(" %c ", mapChar(grid[i][j]));
}
System.out.printf(" %d", i + 1);
System.out.println();
}
printRowEdge();
System.out.println();
}
private static void printRowEdge() {
System.out.print("0 ");
IntStream.range(1, GRID_SIZE + 1).forEach(i -> System.out.printf(" %s ", i));
System.out.print(" 0");
}
private static char mapChar(int i) {
if (i == 3 || i == 100) {
return '*';
}
if (i == 30 || i == 1000) {
return '#';
}
return ' ';
}
private static Coordinate readUntilValidCoordinates(Scanner scanner, int[][] grid) {
boolean coordinateInRange = false;
Coordinate coordinate = null;
while (!coordinateInRange) {
coordinate = readCoordinate(scanner);
if (coordinate.x <= 0 || coordinate.x > GRID_SIZE
|| coordinate.y <= 0 || coordinate.y > GRID_SIZE
|| grid[coordinate.x - 1][coordinate.y - 1] != 0) {
System.out.println("ILLEGAL COORDS. RETYPE");
} else {
coordinateInRange = true;
}
}
return coordinate;
}
private static Coordinate readCoordinate(Scanner scanner) {
Coordinate coordinate = null;
int x, y;
boolean valid = false;
System.out.println("X,Y");
System.out.print("XXXXXX\r");
System.out.print("$$$$$$\r");
System.out.print("&&&&&&\r");
while (!valid) {
try {
System.out.print("? ");
y = scanner.nextInt();
x = scanner.nextInt();
valid = true;
coordinate = new Coordinate(x, y);
} catch (InputMismatchException e) {
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
valid = false;
} finally {
scanner.nextLine();
}
}
return coordinate;
}
private static void printIntro() {
System.out.println(" LIFE2");
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n");
System.out.println("\tU.B. LIFE GAME");
}
private static class Coordinate {
private final int x, y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "Coordinate{" +
"x=" + x +
", y=" + y +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coordinate that = (Coordinate) o;
return x == that.x && y == that.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
private static class Scores {
private final int player1Score;
private final int player2Score;
public Scores(int player1Score, int player2Score) {
this.player1Score = player1Score;
this.player2Score = player2Score;
}
public int getPlayer1Score() {
return player1Score;
}
public int getPlayer2Score() {
return player2Score;
}
}
}

View File

@@ -0,0 +1,155 @@
'''
LIFE FOR TWO
Competitive Game of Life (two or more players).
Ported by Sajid Sarker (2022).
'''
# Global Variable Initialisation
gn = []
gx = []
gy = []
gk = [0, 3, 102, 103, 120, 130, 121, 112, 111, 12, 21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012]
ga = [0, -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1]
m2 = 0
m3 = 0
# Initialise the board
for j in range(6):
gn.append([])
for k in range(6):
gn[j].append(0)
for i in range(3):
gx.append(0)
gy.append(0)
# Helper Functions
def tab(number) -> str:
t = ""
while len(t) < number:
t += " "
return t
def display_header() -> None:
print("{}LIFE2".format(tab(33)))
print("{}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n".format(tab(15)))
print("{}U.B. LIFE GAME".format(tab(10)))
# Board Functions
def setup_board() -> None:
# Players add symbols to initially setup the board
for b in range(1, 3):
p1 = 3 if b != 2 else 30
print("\nPLAYER {} - 3 LIVE PIECES.".format(b))
for k1 in range(1, 4):
query_player(b)
gn[gx[b]][gy[b]] = p1
def modify_board() -> None:
# Players take turns to add symbols and modify the board
for b in range(1, 3):
print("PLAYER {} ".format(b))
query_player(b)
if b == 99:
break
if b <= 2:
gn[gx[1]][gy[1]] = 100
gn[gx[2]][gy[2]] = 1000
def simulate_board() -> None:
# Simulate the board for one step
for j in range(1, 6):
for k in range(1, 6):
if gn[j][k] > 99:
b = 1 if gn[j][k] <= 999 else 10
for o1 in range(1, 16, 2):
gn[j + ga[o1] - 1][k + ga[o1 + 1] - 1] = gn[j + ga[o1] - 1][k + ga[o1 + 1] - 1] + b
#gn[j + ga[o1]][k + ga[o1 + 1]] = gn[j + ga[o1]][k + ga[o1 + 1]] + b
def display_board() -> None:
# Draws the board with all symbols
m2, m3 = 0, 0
for j in range(7):
print("")
for k in range(7):
if j == 0 or j == 6:
if k != 6:
print(" " + str(k) + " ", end="")
else:
print(" 0 ", end="")
elif k == 0 or k == 6:
if j != 6:
print(" " + str(j) + " ", end="")
else:
print(" 0\n")
else:
if gn[j][k] < 3:
gn[j][k] = 0
print(" ", end="")
else:
for o1 in range(1, 19):
if gn[j][k] == gk[o1]:
break
if o1 <= 18:
if o1 > 9:
gn[j][k] = 1000
m3 += 1
print(" # ", end="")
else:
gn[j][k] = 100
m2 += 1
print(" * ", end="")
else:
gn[j][k] = 0
print(" ", end="")
# Player Functions
def query_player(b) -> None:
# Query player for symbol placement coordinates
while True:
print("X,Y\nXXXXXX\n$$$$$$\n&&&&&&")
a_ = input("??")
b_ = input("???")
x_ = [int(num) for num in a_.split() if num.isdigit()]
y_ = [int(num) for num in b_.split() if num.isdigit()]
x_ = [0] if len(x_) == 0 else x_
y_ = [0] if len(y_) == 0 else y_
gx[b] = y_[0]
gy[b] = x_[0]
if gx[b] in range(1, 6) and gy[b] in range(1, 6) and gn[gx[b]][gy[b]] == 0:
break
print("ILLEGAL COORDS. RETYPE")
if b != 1:
if gx[1] == gx[2] and gy[1] == gy[2]:
print("SAME COORD. SET TO 0")
gn[gx[b] + 1][gy[b] + 1] = 0
b = 99
# Game Functions
def check_winner(m2, m3) -> None:
# Check if the game has been won
if m2 == 0 and m3 == 0:
print("\nA DRAW\n")
return
if m3 == 0:
print("\nPLAYER 1 IS THE WINNER\n")
return
if m2 == 0:
print("\nPLAYER 2 IS THE WINNER\n")
return
# Program Flow
def main() -> None:
display_header()
setup_board()
display_board()
while True:
print("\n")
simulate_board()
display_board()
check_winner(m2, m3)
modify_board()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,64 @@
namespace OneCheck;
internal class Board
{
private readonly bool[][] _checkers;
private int _pieceCount;
private int _moveCount;
public Board()
{
_checkers =
Enumerable.Range(0, 8)
.Select(r => Enumerable.Range(0, 8)
.Select(c => r <= 1 || r >= 6 || c <= 1 || c >= 6).ToArray())
.ToArray();
_pieceCount = 48;
}
private bool this[int index]
{
get => _checkers[index / 8][index % 8];
set => _checkers[index / 8][index % 8] = value;
}
public bool PlayMove(IReadWrite io)
{
while (true)
{
var from = (int)io.ReadNumber(Prompts.From);
if (from == 0) { return false; }
var move = new Move { From = from - 1, To = (int)io.ReadNumber(Prompts.To) - 1 };
if (TryMove(move))
{
_moveCount++;
return true;
}
io.Write(Streams.IllegalMove);
}
}
public bool TryMove(Move move)
{
if (move.IsInRange && move.IsTwoSpacesDiagonally && IsPieceJumpingPieceToEmptySpace(move))
{
this[move.From] = false;
this[move.Jumped] = false;
this[move.To] = true;
_pieceCount--;
return true;
}
return false;
}
private bool IsPieceJumpingPieceToEmptySpace(Move move) => this[move.From] && this[move.Jumped] && !this[move.To];
public string GetReport() => string.Format(Formats.Results, _moveCount, _pieceCount);
public override string ToString() =>
string.Join(Environment.NewLine, _checkers.Select(r => string.Join(" ", r.Select(c => c ? " 1" : " 0"))));
}

View File

@@ -0,0 +1,45 @@
namespace OneCheck;
internal class Game
{
private readonly IReadWrite _io;
public Game(IReadWrite io)
{
_io = io;
}
public void Play()
{
_io.Write(Streams.Introduction);
do
{
var board = new Board();
do
{
_io.WriteLine(board);
_io.WriteLine();
} while (board.PlayMove(_io));
_io.WriteLine(board.GetReport());
} while (_io.ReadYesNo(Prompts.TryAgain) == "yes");
_io.Write(Streams.Bye);
}
}
internal static class IOExtensions
{
internal static string ReadYesNo(this IReadWrite io, string prompt)
{
while (true)
{
var response = io.ReadString(prompt).ToLower();
if (response == "yes" || response == "no") { return response; }
io.Write(Streams.YesOrNo);
}
}
}

View File

@@ -0,0 +1,13 @@
namespace OneCheck;
internal class Move
{
public int From { get; init; }
public int To { get; init; }
public int Jumped => (From + To) / 2;
public bool IsInRange => From >= 0 && From <= 63 && To >= 0 && To <= 63;
public bool IsTwoSpacesDiagonally => RowDelta == 2 && ColumnDelta == 2;
private int RowDelta => Math.Abs(From / 8 - To / 8);
private int ColumnDelta => Math.Abs(From % 8 - To % 8);
}

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,5 @@
global using Games.Common.IO;
global using static OneCheck.Resources.Resource;
using OneCheck;
new Game(new ConsoleIO()).Play();

View File

@@ -0,0 +1,2 @@
O.K. Hope you had fun!!

View File

@@ -0,0 +1 @@
Jump from

View File

@@ -0,0 +1 @@
Illegal move. Try again...

View File

@@ -0,0 +1,30 @@
One Check
Creative Computing Morristown, New Jersey
Solitaire checker puzzle by David Ahl
48 checkers and placed on the 2 outside spaces of a
standard 64-square checkerboard. The object is to
remove as many checkers as possible by diagonal jumps
(as in standard checkers). Use the numbered board to
indicate the square you wish to jump from and to. On
the board printed out on each turn '1' indicates a
checker and '0' an empty square. When you have no
possible jumps remaining, input a '0' in response to
question 'Jump from ?'
Here is the numerical board:
1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48
49 50 51 52 53 54 55 56
57 58 59 60 61 62 63 64
And here is the opening position of the checkers.

View File

@@ -0,0 +1,45 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace OneCheck.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Introduction => GetStream();
public static Stream IllegalMove => GetStream();
public static Stream YesOrNo => GetStream();
public static Stream Bye => GetStream();
}
internal static class Formats
{
public static string Results => GetString();
}
internal static class Prompts
{
public static string From => GetString();
public static string To => GetString();
public static string TryAgain => GetString();
}
internal static class Strings
{
public static string TooManyColumns => GetString();
public static string TooManyRows => GetString();
}
private static string GetString([CallerMemberName] string? name = null)
{
using var stream = GetStream(name);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1,3 @@
You made {0} jumps and had {1} pieces
remaining on the board.

View File

@@ -0,0 +1 @@
to

View File

@@ -0,0 +1 @@
Try again

View File

@@ -0,0 +1 @@
Please answer 'Yes' or 'No'.

View File

@@ -0,0 +1,58 @@
print [[
RUSSIAN ROULETTE
CREATIVE COMPUTING MORRISTOWN, NEW JERSY
This is a game of >>>>>>>>>>Russian Roulette
Here is a Revolver
]]
local function parse_input()
local incorrect_input = true
local input = nil
while incorrect_input do
input = io.read(1)
if input == "1" or input == "2" then incorrect_input = false end
end
return input
end
local function russian_roulette()
local NUMBER_OF_ROUNDS = 9
while true do
local dead = false
local n = 0
print("Type '1' to Spin chamber and pull trigger")
print("Type '2' to Give up")
print("Go")
while not dead do
local choice = parse_input()
if choice == "2" then break end
if math.random() > 0.833333333333334 then
dead = true
else
print("CLICK")
n = n + 1
end
if n > NUMBER_OF_ROUNDS then break end
end
if dead then
print("BANG!!!!! You're Dead!")
print("Condolences will be sent to your relatives.\n\n\n")
print("...Next victim...")
elseif n > NUMBER_OF_ROUNDS then
print("You win!!!!!")
print("Let someone else blow his brain out.\n")
else
print(" Chicken!!!!!\n\n\n")
print("...Next victim....")
end
end
end
russian_roulette()