mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
Merge pull request #478 from veelo/highiq
Add D version of High_IQ (48).
This commit is contained in:
2
48_High_IQ/d/.gitignore
vendored
Normal file
2
48_High_IQ/d/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.exe
|
||||||
|
*.obj
|
||||||
214
48_High_IQ/d/README.md
Normal file
214
48_High_IQ/d/README.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||||
|
|
||||||
|
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
|
||||||
|
|
||||||
|
## Running the code
|
||||||
|
|
||||||
|
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
|
||||||
|
```shell
|
||||||
|
dmd -dip1000 -run highiq.d
|
||||||
|
```
|
||||||
|
|
||||||
|
[Other compilers](https://dlang.org/download.html) also exist.
|
||||||
|
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
The original BASIC game code made use of calculus and clever choises of field IDs to determine the validity of moves.
|
||||||
|
This is the original layout of IDs over the board:
|
||||||
|
|
||||||
|
```
|
||||||
|
13 14 15
|
||||||
|
|
||||||
|
22 23 24
|
||||||
|
|
||||||
|
29 30 31 32 33 34 35
|
||||||
|
|
||||||
|
38 39 40 41 42 43 44
|
||||||
|
|
||||||
|
47 48 49 50 51 52 53
|
||||||
|
|
||||||
|
58 59 60
|
||||||
|
|
||||||
|
67 68 69
|
||||||
|
```
|
||||||
|
|
||||||
|
This seems not very logical, because, wouldn't it make much more sense to let columns increase with 1 and rows increase
|
||||||
|
with 10, so you'd get a consistent coordinate system? It seems that the original author's first step in validating
|
||||||
|
moves was to check that moves jumped from one field over another one onto the next. He did this by making sure that
|
||||||
|
adjacent IDs alter between even and odd horizontally *and* vertically. So a valid move was always from an even ID to an
|
||||||
|
even ID *or* from an odd ID to an odd ID. So one of the checks that the BASIC code made was that the sum of both IDs
|
||||||
|
was even. This is of course not a sufficient test, because moves that jump over three fields are illegal. Therefore the
|
||||||
|
IDs seem to have been carefully laid oud so that the IDs increase with 1 horizontally, and 9 vertically, everywhere. So
|
||||||
|
the only valid difference between IDs for a horizontal move was always 2, and the only valid difference for a vertical
|
||||||
|
move was always 18.
|
||||||
|
|
||||||
|
Fact of the matter is, however, that checking for difference is sufficient and the even sum rule is superfluous, so
|
||||||
|
there is no need for the peculiar distribution of field IDs. Therefore I have chosen the following more logical
|
||||||
|
distribution:
|
||||||
|
|
||||||
|
```
|
||||||
|
13 14 15
|
||||||
|
|
||||||
|
23 24 25
|
||||||
|
|
||||||
|
31 32 33 34 35 36 37
|
||||||
|
|
||||||
|
41 42 43 44 45 46 47
|
||||||
|
|
||||||
|
51 52 53 54 55 56 57
|
||||||
|
|
||||||
|
63 64 65
|
||||||
|
|
||||||
|
73 74 75
|
||||||
|
```
|
||||||
|
|
||||||
|
As a consequence, the implementation of the game code has become much simpler; Not alone due to one less check, but due
|
||||||
|
to the fact that conversions between IDs and board coordinates have become unnecessary and thus we can work with a single
|
||||||
|
representation of the board state.
|
||||||
|
|
||||||
|
This version makes a prettier print of the board than the BASIC original, with coordinates for every move, and explains
|
||||||
|
illegal moves.
|
||||||
|
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
```
|
||||||
|
H-I-Q
|
||||||
|
(After Creative Computing Morristown, New Jersey)
|
||||||
|
|
||||||
|
|
||||||
|
Fields are identified by 2-digit numbers, each
|
||||||
|
between 1 and 7. Example: the middle field is 44,
|
||||||
|
the bottom middle is 74.
|
||||||
|
|
||||||
|
_1 _2 _3 _4 _5 _6 _7
|
||||||
|
┌───┬───┬───┐
|
||||||
|
1_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
2_ │ ■ │ ■ │ ■ │
|
||||||
|
┌───┬───┼───┼───┼───┼───┬───┐
|
||||||
|
3_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┼───┼───┼───┼───┴───┘
|
||||||
|
6_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
7_ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┴───┘
|
||||||
|
|
||||||
|
Move which peg? 23
|
||||||
|
The peg at 23 has nowhere to go. Try again.
|
||||||
|
|
||||||
|
Move which peg? 24
|
||||||
|
To where? 34
|
||||||
|
Field 34 is occupied. Try again.
|
||||||
|
To where? 54
|
||||||
|
Field 54 is occupied. Try again.
|
||||||
|
To where? 44
|
||||||
|
|
||||||
|
_1 _2 _3 _4 _5 _6 _7
|
||||||
|
┌───┬───┬───┐
|
||||||
|
1_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
2_ │ ■ │ │ ■ │
|
||||||
|
┌───┬───┼───┼───┼───┼───┬───┐
|
||||||
|
3_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┼───┼───┼───┼───┴───┘
|
||||||
|
6_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
7_ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┴───┘
|
||||||
|
|
||||||
|
Move which peg? 14
|
||||||
|
The peg at 14 has nowhere to go. Try again.
|
||||||
|
|
||||||
|
Move which peg? 24
|
||||||
|
There is no peg at 24. Try again.
|
||||||
|
|
||||||
|
Move which peg? 44
|
||||||
|
The peg at 44 has nowhere to go. Try again.
|
||||||
|
|
||||||
|
Move which peg? 32
|
||||||
|
To where? 22
|
||||||
|
Field 22 is ouside the board. Try again.
|
||||||
|
To where? 33
|
||||||
|
Field 33 is occupied. Try again.
|
||||||
|
To where? 34
|
||||||
|
|
||||||
|
_1 _2 _3 _4 _5 _6 _7
|
||||||
|
┌───┬───┬───┐
|
||||||
|
1_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
2_ │ ■ │ │ ■ │
|
||||||
|
┌───┬───┼───┼───┼───┼───┬───┐
|
||||||
|
3_ │ ■ │ │ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┼───┼───┼───┼───┴───┘
|
||||||
|
6_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
7_ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┴───┘
|
||||||
|
|
||||||
|
Move which peg? 44
|
||||||
|
To where? 33
|
||||||
|
You cannot move diagonally. Try again.
|
||||||
|
To where? 24
|
||||||
|
|
||||||
|
_1 _2 _3 _4 _5 _6 _7
|
||||||
|
┌───┬───┬───┐
|
||||||
|
1_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
2_ │ ■ │ ■ │ ■ │
|
||||||
|
┌───┬───┼───┼───┼───┼───┬───┐
|
||||||
|
3_ │ ■ │ │ │ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┼───┼───┼───┼───┴───┘
|
||||||
|
6_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
7_ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┴───┘
|
||||||
|
|
||||||
|
Move which peg? 36
|
||||||
|
To where? 33
|
||||||
|
You can't jump that far. Try again.
|
||||||
|
To where? 35
|
||||||
|
Field 35 is occupied. Try again.
|
||||||
|
To where? 34
|
||||||
|
|
||||||
|
_1 _2 _3 _4 _5 _6 _7
|
||||||
|
┌───┬───┬───┐
|
||||||
|
1_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
2_ │ ■ │ ■ │ ■ │
|
||||||
|
┌───┬───┼───┼───┼───┼───┬───┐
|
||||||
|
3_ │ ■ │ │ │ ■ │ │ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
4_ │ ■ │ ■ │ ■ │ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┼───┼───┼───┼───┤
|
||||||
|
5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┼───┼───┼───┼───┴───┘
|
||||||
|
6_ │ ■ │ ■ │ ■ │
|
||||||
|
├───┼───┼───┤
|
||||||
|
7_ │ ■ │ ■ │ ■ │
|
||||||
|
└───┴───┴───┘
|
||||||
|
|
||||||
|
Move which peg? 46
|
||||||
|
To where? 36
|
||||||
|
You need to jump over another peg. Try again.
|
||||||
|
To where? down
|
||||||
|
Field 00 is ouside the board. Try again.
|
||||||
|
To where?
|
||||||
|
```
|
||||||
238
48_High_IQ/d/highiq.d
Normal file
238
48_High_IQ/d/highiq.d
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
@safe: // Make @safe the default for this file, enforcing memory-safety.
|
||||||
|
import std;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
enum width = 50;
|
||||||
|
writeln(center("H-I-Q", width));
|
||||||
|
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width));
|
||||||
|
writeln(wrap("Fields are identified by 2-digit numbers, each between 1 and 7. " ~
|
||||||
|
"Example: the middle field is 44, the bottom middle is 74.", width));
|
||||||
|
|
||||||
|
Board board;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while (!board.isGameOver)
|
||||||
|
{
|
||||||
|
writeln(board); // Calls board.toString().
|
||||||
|
board.makeMove;
|
||||||
|
}
|
||||||
|
writeln(board, "\nThe game is over.\nYou had ", board.numPegs, " pieces remaining.");
|
||||||
|
if (board.numPegs == 1)
|
||||||
|
writeln("Bravo! You made a perfect score!\n",
|
||||||
|
"Make a screen dump as a record of your accomplishment!\n");
|
||||||
|
write("Play again? (Yes or No) ");
|
||||||
|
if (readString.toLower != "yes")
|
||||||
|
break;
|
||||||
|
writeln; writeln;
|
||||||
|
board = Board.init;
|
||||||
|
}
|
||||||
|
writeln("\nSo long for now.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Representation of the game board with pegs.
|
||||||
|
struct Board
|
||||||
|
{
|
||||||
|
enum {outside, taken, empty};
|
||||||
|
int[8][8] state = [
|
||||||
|
1: [ 3: taken, 4: taken, 5: taken],
|
||||||
|
2: [ 3: taken, 4: taken, 5: taken],
|
||||||
|
3: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
|
||||||
|
4: [1: taken, 2: taken, 3: taken, 4: empty, 5: taken, 6: taken, 7: taken],
|
||||||
|
5: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
|
||||||
|
6: [ 3: taken, 4: taken, 5: taken],
|
||||||
|
7: [ 3: taken, 4: taken, 5: taken]
|
||||||
|
]; // Row 0 and column 0 are unused. Default is 0 (outside).
|
||||||
|
|
||||||
|
/// Returns a string representing the board and its current state.
|
||||||
|
string toString() const
|
||||||
|
{
|
||||||
|
dchar[][] lines = [(" _1 _2 _3 _4 _5 _6 _7 ").to!(dchar[]),
|
||||||
|
(" ┌───┬───┬───┐ ").to!(dchar[]),
|
||||||
|
(" 1_ │ │ │ │ ").to!(dchar[]),
|
||||||
|
(" ├───┼───┼───┤ ").to!(dchar[]),
|
||||||
|
(" 2_ │ │ │ │ ").to!(dchar[]),
|
||||||
|
(" ┌───┬───┼───┼───┼───┼───┬───┐").to!(dchar[]),
|
||||||
|
(" 3_ │ │ │ │ │ │ │ │").to!(dchar[]),
|
||||||
|
(" ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
|
||||||
|
(" 4_ │ │ │ │ │ │ │ │").to!(dchar[]),
|
||||||
|
(" ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
|
||||||
|
(" 5_ │ │ │ │ │ │ │ │").to!(dchar[]),
|
||||||
|
(" └───┴───┼───┼───┼───┼───┴───┘").to!(dchar[]),
|
||||||
|
(" 6_ │ │ │ │ ").to!(dchar[]),
|
||||||
|
(" ├───┼───┼───┤ ").to!(dchar[]),
|
||||||
|
(" 7_ │ │ │ │ ").to!(dchar[]),
|
||||||
|
(" └───┴───┴───┘ ").to!(dchar[])];
|
||||||
|
foreach (y, row; state)
|
||||||
|
foreach (x, field; row)
|
||||||
|
if (field == taken)
|
||||||
|
lines[y * 2][x * 4 + 2] = '■';
|
||||||
|
return lines.join("\n").to!string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests for possible moves.
|
||||||
|
bool isGameOver() const
|
||||||
|
{
|
||||||
|
foreach (r, row; state)
|
||||||
|
foreach (c, field; row)
|
||||||
|
if (field == taken && canMoveFrom(r, c))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canMoveFrom(int row, int col) const
|
||||||
|
{
|
||||||
|
if (row >= 3 && state[row - 2][col] == empty) // Up
|
||||||
|
return state[row - 1][col] == taken;
|
||||||
|
if (row <= 5 && state[row + 2][col] == empty) // Down
|
||||||
|
return state[row + 1][col] == taken;
|
||||||
|
if (col >= 3 && state[row][col - 2] == empty) // Left
|
||||||
|
return state[row][col - 1] == taken;
|
||||||
|
if (col <= 5 && state[row][col + 2] == empty) // Right
|
||||||
|
return state[row][col + 1] == taken;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asks for input, validates the move and updates the board.
|
||||||
|
void makeMove()
|
||||||
|
{
|
||||||
|
bool isOutside(int row, int col)
|
||||||
|
{
|
||||||
|
if (row < 1 || row > 7 ||
|
||||||
|
col < 1 || col > 7 ||
|
||||||
|
state[row][col] == outside)
|
||||||
|
{
|
||||||
|
writeln("Field ", row, col, " is ouside the board. Try again.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto from = (){
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
write("\nMove which peg? ");
|
||||||
|
int field = readInt;
|
||||||
|
int row = field / 10, col = field % 10;
|
||||||
|
if (isOutside(row, col))
|
||||||
|
continue;
|
||||||
|
if (state[row][col] != taken)
|
||||||
|
{
|
||||||
|
writeln("There is no peg at ", field, ". Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!canMoveFrom(row, col))
|
||||||
|
{
|
||||||
|
writeln("The peg at ", field, " has nowhere to go. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return tuple!("row", "col")(row, col);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
auto to = (){
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
write("To where? ");
|
||||||
|
int field = readInt;
|
||||||
|
int row = field / 10, col = field % 10;
|
||||||
|
if (isOutside(row, col))
|
||||||
|
continue;
|
||||||
|
if (state[row][col] == taken)
|
||||||
|
{
|
||||||
|
writeln("Field ", field, " is occupied. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (row != from.row && col != from.col)
|
||||||
|
{
|
||||||
|
writeln("You cannot move diagonally. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (row == from.row && col == from.col)
|
||||||
|
{
|
||||||
|
writeln("You aren't going anywhere. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (abs(row - from.row) + abs(col - from.col) > 2)
|
||||||
|
{
|
||||||
|
writeln("You can't jump that far. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (abs(row - from.row) + abs(col - from.col) < 2 ||
|
||||||
|
state[(row + from.row) / 2][(col + from.col) / 2] != taken)
|
||||||
|
{
|
||||||
|
writeln("You need to jump over another peg. Try again.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return tuple!("row", "col")(row, col);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
// The move is legal. Update the board state.
|
||||||
|
state[from.row][from.col] = empty;
|
||||||
|
state[ to.row][ to.col] = taken;
|
||||||
|
state[(from.row + to.row) / 2][(from.col + to.col) / 2] = empty;
|
||||||
|
writeln;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of remaining pegs on the board.
|
||||||
|
int numPegs() const
|
||||||
|
{
|
||||||
|
int num = 0;
|
||||||
|
foreach (row; state)
|
||||||
|
foreach (field; row)
|
||||||
|
if (field == taken)
|
||||||
|
num++;
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads an integer from standard input.
|
||||||
|
int readInt() nothrow
|
||||||
|
{
|
||||||
|
try
|
||||||
|
return readString.to!int;
|
||||||
|
catch (Exception) // Not an integer.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a string from standard input.
|
||||||
|
string readString() nothrow
|
||||||
|
{
|
||||||
|
try
|
||||||
|
return trustedReadln.strip;
|
||||||
|
catch (Exception) // readln throws on I/O and Unicode errors, which we handle here.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An @trusted wrapper around readln.
|
||||||
|
*
|
||||||
|
* This is the only function that formally requires manual review for memory-safety.
|
||||||
|
* [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
|
||||||
|
* which would remove the need to have any @trusted code in this program.
|
||||||
|
*/
|
||||||
|
string trustedReadln() @trusted
|
||||||
|
{
|
||||||
|
return readln;
|
||||||
|
}
|
||||||
|
|
||||||
|
version (Windows)
|
||||||
|
{
|
||||||
|
// Make the Windows console do a better job at printing UTF-8 strings,
|
||||||
|
// and restore the default upon termination.
|
||||||
|
|
||||||
|
import core.sys.windows.windows;
|
||||||
|
|
||||||
|
shared static this() @trusted
|
||||||
|
{
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
shared static ~this() @trusted
|
||||||
|
{
|
||||||
|
SetConsoleOutputCP(GetACP);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user