Skip to content

Commit

Permalink
Merge pull request #3 from noncombatant/main
Browse files Browse the repository at this point in the history
Main
  • Loading branch information
noncombatant authored May 14, 2023
2 parents 58155f8 + 25e42a5 commit 22c2bea
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CC = clang
CFLAGS = -Weverything -Werror -O2 -std=c17 -Wno-padded -Wno-poison-system-directories
CFLAGS = -Weverything -Werror -O3 -std=c2x -Wno-padded -Wno-poison-system-directories -Wno-declaration-after-statement
LDFLAGS = -lncurses

robotfindskitten: robotfindskitten.c
Expand Down
21 changes: 7 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
About
=====
# About

robotfindskitten is a very fun adventure game for robots and humans. There are
many versions of the game (you can see them all at
Expand All @@ -16,8 +15,7 @@ magnitude larger than the code itself. That seemed wrong.
By perusing the Git history, you can see the steps I followed to get from
Alexey’s original to this version.

Building And Installing
=======================
## Building And Installing

This version of robotfindskitten works on POSIX operating systems (tested on
Ubuntu and macOS) with reasonably modern C compilers (tested with Clang).
Expand Down Expand Up @@ -46,8 +44,7 @@ To install, do the preceeding and then copy the robotfindskitten binary to a
place in your `$PATH`. A good place might be `$HOME/bin`, `/usr/games`, or
`/usr/local/games`.

Learning C With robotfindskitten
================================
## Learning C With robotfindskitten

In my experience, a good way to learn how to learn a programming language is to
interleave the activities of reading tutorial books, reading code written in the
Expand All @@ -73,8 +70,7 @@ change the game play in ways you find fun. Here are some ideas:
rather than staying stationary
* ...whatever else seems fun to you!

Basics Of Reading C
-------------------
### Basics Of Reading C

I assume that most people reading this are coming from a background programming
in a very high-level language (VHLL) like Python, JavaScript, or Ruby. C is
Expand Down Expand Up @@ -118,8 +114,7 @@ programmers, and somewhat familiar to Python programmers.
You probably won’t understand everything right away, and that is OK. Like Robot,
you will find your Kitten with patience and in due time.

Understanding The C Standard Library
------------------------------------
### Understanding The C Standard Library

So much for the grammar of C. What about the vocabulary? Like all programming
languages, C has a standard library of functions and data types that programmers
Expand Down Expand Up @@ -151,8 +146,7 @@ you when implementing changes to robotfindskitten:
arrays
* `fopen`, `fread`, `fwrite`, `fclose`, and `getline`, for working with files

A Note On Names
---------------
### A Note On Names

You’ll notice when reading the code that identifiers (variable names, function
names, type names, et c.) seem to come from multiple naming conventions. That is
Expand All @@ -174,8 +168,7 @@ used in this code:
* `i` and `j` for loop iterator variables (or other ‘obvious’ names, like
`Object o`)

Further Reading
---------------
### Further Reading

[“Writing Programs With NCURSES” by Raymond and
Ben-Halim](http://invisible-island.net/ncurses/ncurses-intro.html) describes the
Expand Down
31 changes: 29 additions & 2 deletions non_kitten_items.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
static char* Messages[] = {
static const char* Messages[] = {
// Do not change these:
"",
"",
Expand Down Expand Up @@ -960,7 +960,7 @@ static char* Messages[] = {
"A suspended 9th chord. Love that sound.",
"This is the sine qua non of non-kitten items.",
"Etched in stone, the words “TEXAS IRRITANDVM EST”.",
"You find some NOS germanium capacitors. Now you can repair your fuzzing unit!",
"You find some NOS germanium transistors. Now you can repair your fuzzing unit!",
"A scratched scratching post. Kitten has been here.",
"Kitten spoor.",
"It’s you!",
Expand All @@ -969,6 +969,33 @@ static char* Messages[] = {
"This appears to be a 21-sided die. The AD&D rules get weirder every year...",
"A freshly-reinvented wheel that suddenly disappears.",
"Nothing here but the wind.",
"It’s a tracking cookie. It tastes like cardboard.",
"You find an AddressSanitizer report. There was once a bug here.",
"When you look under this rock, you don’t see kitten.",
"In the grand scheme of things, it’s nothing. Don’t worry about it!",
"A few scattered Unicode code points.",
"A child’s birthday party is in progress. There is cake icing everywhere.",
"You have no idea what this is. Neither does it?",
"A wizard’s spellbook.",
"Thieves’ tools.",
"A lute. You twang it.",
"When you wave this magic wand, it sputters.",
"A rusted knight’s helmet. The visor squeaks when you lift it.",
"Monk is here. They are serving tea.",
"It’s the tritone substitution again.",
"A solar-powered water desalinator.",
"Well, it was delicious.",
"A new type of Girl Scout cookie.",
"Loose-leaf catnip, in a small cotton pouch. You save this for kitten.",
"It would seem to be a lambda cube.",
"You’re not sure this new top-level domain name was a good idea.",
"A couple peanuts in their shells. You know a crow who would like this.",
"A commemorative plaque celebrating that time you and Crow saved East St. Louis.",
"A treatise on the middle voice. It reads well.",
"The supine case. “Tam diu minime visu!”, you tell it.",
"The Tao that can be told is not the eternal Tao. This must not be it, then.",
"Doubt gnaws at you, but you press on.",
"Just another opcode.",
};

#define MessageCount (sizeof(Messages) / sizeof(Messages[0]))
95 changes: 45 additions & 50 deletions robotfindskitten.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

#include "non_kitten_items.h"

static const char Version[] = "2.71828182";
static const char Version[] = "2.718281828";
static const char Introduction[] =
"By the illustrious Leonard Richardson © 1997, 2000.\n"
"Written originally for the Nerth Pork robotfindskitten contest.\n"
Expand All @@ -45,7 +45,7 @@ static const char Introduction[] =
"pressing the q key or a good old-fashioned Control-C.\n"
"\n"
"You can move using the arrow keys, the Emacs movement control sequences,\n"
"the vi and Nethack movement keys, or the number keypad.\n"
"the vi and NetHack movement keys, or the number keypad.\n"
"\n"
"Press any key to start.\n";
static const char WinMessage[] = "You found kitten! Way to go, robot!";
Expand All @@ -54,7 +54,7 @@ static const size_t DefaultItemCount = 20;

#define CONTROL(key) ((key)&0x1f)

typedef enum {
typedef enum KeyCode {
NetHack_down = 'j',
NetHack_DOWN = 'J',
NetHack_up = 'k',
Expand Down Expand Up @@ -91,11 +91,11 @@ typedef enum {
Key_QUIT = 'Q',
} KeyCode;

static const int HeaderSize = 2;
static const int HeaderSize = 1;
static const int FrameThickness = 1;
static const unsigned int White = 7;

typedef struct {
typedef struct ScreenObject {
int x;
int y;
unsigned int color;
Expand All @@ -104,35 +104,39 @@ typedef struct {
chtype character;
} ScreenObject;

static struct {
static struct GameState {
int lines;
int columns;
bool screen_has_color;
size_t item_count;
size_t message_count;
ScreenObject items[MessageCount];
char** messages;
const char** messages;
} GameState;

// Special indices in the GameState.items array.
static const size_t Robot = 0;
static const size_t Kitten = 1;
static const size_t Bogus = 2;

static bool StringsEqual(const char* a, const char* b) {
return strcmp(a, b) == 0;
}

static void InitializeMessages(void) {
GameState.messages = Messages;
GameState.message_count = MessageCount;
assert(GameState.message_count > Bogus);
for (size_t i = Bogus; i < (GameState.message_count - 1); ++i) {
size_t j = i + ((size_t)random() % (GameState.message_count - i));
const size_t j = i + ((size_t)random() % (GameState.message_count - i));
if (i != j) {
char* temp = GameState.messages[i];
const char* temp = GameState.messages[i];
GameState.messages[i] = GameState.messages[j];
GameState.messages[j] = temp;
}
}
assert(GameState.message_count > 0);
assert(0 == strcmp("", GameState.messages[Robot]));
assert(0 == strcmp("", GameState.messages[Kitten]));
assert(StringsEqual("", GameState.messages[Robot]));
assert(StringsEqual("", GameState.messages[Kitten]));
}

static int RandomX(void) {
Expand All @@ -153,14 +157,14 @@ static unsigned int RandomColor(void) {
}

static chtype RandomCharacter(void) {
chtype c;
chtype c = '\0';
do {
c = (chtype)((random() % ('~' - '!' + 1) + '!'));
} while (c == '#');
return c;
}

static bool ScreenObjectEqual(const ScreenObject a, const ScreenObject b) {
static bool ScreenObjectsEqual(const ScreenObject a, const ScreenObject b) {
return a.x == b.x && a.y == b.y;
}

Expand Down Expand Up @@ -194,7 +198,6 @@ static noreturn void Finish(int signal) {

static void InitializeGame(size_t item_count) {
InitializeMessages();
item_count = GameState.message_count ? item_count : GameState.message_count;
GameState.item_count = Bogus + item_count;

signal(SIGINT, Finish);
Expand Down Expand Up @@ -228,7 +231,7 @@ static void InitializeGame(size_t item_count) {
do {
GameState.items[Kitten].y = RandomY();
GameState.items[Kitten].x = RandomX();
} while (ScreenObjectEqual(GameState.items[Robot], GameState.items[Kitten]));
} while (ScreenObjectsEqual(GameState.items[Robot], GameState.items[Kitten]));

for (size_t i = Bogus; i < GameState.item_count; ++i) {
GameState.items[i].character = RandomCharacter();
Expand All @@ -237,15 +240,15 @@ static void InitializeGame(size_t item_count) {
while (true) {
GameState.items[i].y = RandomY();
GameState.items[i].x = RandomX();
if (ScreenObjectEqual(GameState.items[Robot], GameState.items[i])) {
if (ScreenObjectsEqual(GameState.items[Robot], GameState.items[i])) {
continue;
}
if (ScreenObjectEqual(GameState.items[Kitten], GameState.items[i])) {
if (ScreenObjectsEqual(GameState.items[Kitten], GameState.items[i])) {
continue;
}
size_t j;
for (j = 0; j < i; ++j) {
if (ScreenObjectEqual(GameState.items[j], GameState.items[i])) {
if (ScreenObjectsEqual(GameState.items[j], GameState.items[i])) {
break;
}
}
Expand Down Expand Up @@ -297,9 +300,9 @@ static void ShowMessage(const char* message) {
if (GameState.screen_has_color) {
attrset(COLOR_PAIR(White));
}
move(1, 0);
move(0, 0);
clrtoeol();
move(1, 0);
move(0, 0);
printw("%.*s", GameState.columns, message);
move(y, x);
refresh();
Expand All @@ -314,17 +317,15 @@ static void RedrawScreen(void) {
mvaddch(HeaderSize, COLS - 1, ACS_URCORNER);
mvaddch(LINES - 1, 0, ACS_LLCORNER);
mvaddch(LINES - 1, COLS - 1, ACS_LRCORNER);
for (unsigned int i = 1; i < (unsigned int)COLS - 1; ++i) {
mvaddch(HeaderSize, (int)i, ACS_HLINE);
mvaddch(LINES - 1, (int)i, ACS_HLINE);
for (int i = 1; i < COLS - 1; ++i) {
mvaddch(HeaderSize, i, ACS_HLINE);
mvaddch(LINES - 1, i, ACS_HLINE);
}
for (unsigned int i = FrameThickness + HeaderSize;
i < (unsigned int)LINES - 1; ++i) {
mvaddch((int)i, 0, ACS_VLINE);
mvaddch((int)i, COLS - 1, ACS_VLINE);
for (int i = FrameThickness + HeaderSize; i < LINES - 1; ++i) {
mvaddch(i, 0, ACS_VLINE);
mvaddch(i, COLS - 1, ACS_VLINE);
}
move(0, 0);
printw("robotfindskitten %s\n\n", Version);
for (size_t i = 0; i < GameState.item_count; ++i) {
move(GameState.items[i].y, GameState.items[i].x);
Draw(&GameState.items[i]);
Expand All @@ -338,8 +339,7 @@ static void RedrawScreen(void) {

static void HandleResize(void) {
int xbound = 0, ybound = 0;
unsigned int i;
for (i = 0; i < GameState.item_count; ++i) {
for (size_t i = 0; i < GameState.item_count; ++i) {
if (GameState.items[i].x > xbound) {
xbound = GameState.items[i].x;
}
Expand Down Expand Up @@ -374,9 +374,9 @@ static void ShowIntroduction(void) {
}

static void PlayAnimation(bool approach_from_right) {
move(1, 0);
move(0, 0);
clrtoeol();
int animation_meet = (COLS / 2);
const int animation_meet = (COLS / 2);

ScreenObject kitten;
memcpy(&kitten, &GameState.items[Kitten], sizeof(kitten));
Expand All @@ -397,8 +397,8 @@ static void PlayAnimation(bool approach_from_right) {

GameState.items[Robot].character = (chtype)'#';
GameState.items[Kitten].character = kitty;
GameState.items[Robot].y = 1;
GameState.items[Kitten].y = 1;
GameState.items[Robot].y = 0;
GameState.items[Kitten].y = 0;
if (approach_from_right) {
GameState.items[Robot].x = animation_meet + i;
GameState.items[Kitten].x = animation_meet - i + 1;
Expand Down Expand Up @@ -426,17 +426,17 @@ static void PlayAnimation(bool approach_from_right) {
}

static void MainLoop(void) {
int x, y;
size_t item_number = 0;
bool approach_from_right = false;

while (true) {
int ch = getch();
if (0 == ch) {
const int ch = getch();
if (ch == 0) {
break;
}
y = GameState.items[Robot].y;
x = GameState.items[Robot].x;

int y = GameState.items[Robot].y;
int x = GameState.items[Robot].x;
size_t item_number = 0;
bool approach_from_right = false;

switch (ch) {
case NetHack_UP_LEFT:
case NetHack_up_left:
Expand Down Expand Up @@ -564,19 +564,14 @@ int main(int count, char* arguments[]) {
bool options_present = false;

while (true) {
int option = getopt(count, arguments, "n:s:h");
const int option = getopt(count, arguments, "n:s:h");
if (-1 == option) {
break;
}

switch (option) {
case 'n': {
int i = atoi(optarg);
if (i <= 0) {
fprintf(stderr, "Argument must be positive.\n");
exit(EXIT_FAILURE);
}
item_count = (size_t)i;
item_count = (size_t)abs(atoi(optarg));
options_present = true;
break;
}
Expand Down

0 comments on commit 22c2bea

Please sign in to comment.