Full Game
COP-3223H
Table of Contents
notes
- overwrite_mode
- converting ascii art into c arrays (script)
- #include
txt -> c array generator python script
wget https://www.cs.ucf.edu/~gazzillo/teaching/cop3223hspring26/livedemos/full_game/background.py python3 background.py < background.array > background.c
Non-canonical input
noncanon.c
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int
main (void)
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if (c == '\004') /* C-d */
break;
else
write (STDOUT_FILENO, &c, 1);
}
return EXIT_SUCCESS;
}
Example: rot13
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int
main (void)
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if ('A' <= c && c <= 'Z') {
c = (((c - 'A') + 13) % ('Z' - 'A' + 1)) + 'A';
}
if ('a' <= c && c <= 'z') {
c = (((c - 'a') + 13) % ('z' - 'a' + 1)) + 'a';
}
if (c == '\004') /* C-d */
break;
else
write (STDOUT_FILENO, &c, 1);
}
return EXIT_SUCCESS;
}
State machines
chase.c
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <stdio.h>
#include <stdbool.h>
#include "term.c"
int main(int argc, char **argv) {
set_input_mode ();
printf("\033[?25l"); // hide cursor
int width = 40;
int height = 20;
// player data
// 0 1 2
enum { vulnerable, invincible, dead };
int player_state = vulnerable;
int player_x = width / 2;
int player_y = height / 2;
// enemy data
int enemy_x = 1;
int enemy_y = 1;
enum { chasing, running, defeated };
int enemy_state = chasing;
// potion data
int potion_x = 29;
int potion_y = 11;
int random = 1;
enum { available, using, done };
int potion_state = available;
int potion_counter = 0;
while (1) {
printf("\x1b[2J");
printf("\x1b[H");
// 0 available
// 1 using
// 2 done
// initial state is 0
// switch (potion_state) {
// case 0:
// }
switch (potion_state) {
case available:
printf("\033[%d;%dH", potion_y, potion_x);
printf("/\\");
printf("\033[%d;%dH", potion_y + 1, potion_x);
printf("\\/");
break;
case using:
// intentionally empty
break;
}
printf("\033[%d;%dH", enemy_y, enemy_x);
switch (enemy_state) {
case chasing: // case 0:
putchar('@');
break;
case running: // case 1:
putchar('@');
break;
case defeated: // case 2:
putchar('X');
}
printf("\033[%d;%dH", player_y, player_x);
switch (player_state) {
case vulnerable:
putchar('h');
break;
case invincible:
putchar('H');
break;
case dead:
printf(":(");
}
/* // debugging output */
/* printf("\033[%d;%dH", height + 1, 0); */
/* printf("player_y: %d\n", player_y); */
/* printf("player_x: %d\n", player_x); */
/* printf("enemy_y: %d\n", enemy_y); */
/* printf("enemy_x: %d\n", enemy_x); */
/* printf("potion_y: %d\n", potion_y); */
/* printf("potion_x: %d\n", potion_x); */
// read input
int direction_x = 0;
int direction_y = 0;
bool valid = true;
do {
char c = '\0';
scanf("%c", &c);
switch (c) {
case 'i':
direction_y = -2;
break;
case 'k':
direction_y = 2;
break;
case 'j':
direction_x = -2;
break;
case 'l':
direction_x = 2;
break;
default:
valid = false;
break;
}
} while (! valid);
switch(player_state) {
case vulnerable: // case 0:
player_x += direction_x;
player_y += direction_y;
break;
case invincible: // case 1:
player_x += direction_x;
player_y += direction_y;
if (enemy_x - 1 <= player_x && player_x <= enemy_x + 1 &&
enemy_y - 1 <= player_y && player_y <= enemy_y + 1) {
player_state = vulnerable;
enemy_state = defeated;
potion_state = done;
}
break;
}
switch (enemy_state) {
case chasing: // chasing
if (player_x > enemy_x) {
enemy_x += 1;
}
if (player_x < enemy_x) {
enemy_x -= 1;
}
if (player_y > enemy_y) {
enemy_y += 1;
}
if (player_y < enemy_y) {
enemy_y -= 1;
}
if ((player_x == enemy_x || player_x == enemy_x + 1) &&
(player_y == enemy_y || player_y == enemy_y + 1)) {
player_state = dead;
}
break;
case running:
if (player_x > enemy_x) {
enemy_x -= 1;
}
if (player_x < enemy_x) {
enemy_x += 1;
}
if (player_y > enemy_y) {
enemy_y -= 1;
}
if (player_y < enemy_y) {
enemy_y += 1;
}
if (enemy_x < 0) {
enemy_x = 0;
}
if (enemy_x > width) {
enemy_x = width;
}
if (enemy_y < 0) {
enemy_y = 0;
}
if (enemy_y > height) {
enemy_y = height;
}
break;
}
switch (potion_state) {
case available:
if ((player_x == potion_x || player_x == potion_x + 1) &&
(player_y == potion_y || player_y == potion_y + 1)) {
player_state = invincible;
enemy_state = running;
potion_state = using;
potion_counter = 10;
}
break;
case using:
potion_counter -= 1;
if (potion_counter == 1) {
player_state = vulnerable;
enemy_state = chasing;
potion_state = available;
random = (random * 97) % 65533;
potion_x = random % width;
random = (random * 97) % 65533;
potion_y = random % height;
}
break;
}
}
}
term.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <termios.h>
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
// https://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input
bool kbhit() {
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
if (select(1, &fds, NULL, NULL, &tv) == 1) {
return true;
} else {
return false;
}
}
int getch() {
int r;
unsigned char c;
do {
if ((r = read(0, &c, sizeof(c))) < 0) {
return '\0'; // null character on error
}
} while (r < 1); // ensures read returns one character
return c;
}
Sprite Arrays
Example ascii art
/\ \/
Example array
int potion_sprite_width = 2;
int potion_sprite_height = 2;
char potion_sprite[2][2] = { { '/', '\\' },
{ '\\', '/' } }
Another example array
int potion_sprite_width = 2;
int potion_sprite_height = 3;
char potion_sprite[3][2] = { { 'w', 'w' },
{ '|', '|' },
{ '/', '\\' } };
Drawing the array
for (int j = 0; j < potion_sprite_height; j++) {
printf("\033[%d;%dH", potion_y + j, potion_x);
for (int i = 0; i < potion_sprite_width; i++) {
putchar(potion_sprite[j][i]);
}
}
In-class coding
/* int potion_sprite_width = 2; */
/* int potion_sprite_height = 2; */
/* // y x */
/* // 0 0 */
/* // 0 1 */
/* // 1 0 */
/* // 1 1 */
/* char potion_sprite[2][2] = { { '/', '\\' }, */
/* { '\\', '/' } }; */
int potion_sprite_width = 2;
int potion_sprite_height = 3;
char potion_sprite[3][2] = { { 'w', 'w' },
{ '|', '|' },
{ '/', '\\' } };
/*
escape characters
backlash is the escape character
\n
\0134
*/
int sprite_x = 10;
int sprite_y = 30;
// how we move to y, x coordinates
// j, i position within the array
// y, x position on the screen
// 0, 1
for (int j = 0; j < potion_sprite_height; j++) {
// 0, 1
for (int i = 0; i < potion_sprite_width; i++) {
int x = sprite_x + i;
int y = sprite_y + j;
// positioning the cursor
printf("\033[%d;%dH", y, x);
/* printing each y, x element of the array */
putchar(potion_sprite[j][i]);
}
}
// y x
// 1 1
// 1 2
// 2 1
// 2 2
printf("\n");
exit(1);
Chase
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <stdio.h>
#include <stdbool.h>
#include "term.c"
int main(int argc, char **argv) {
set_input_mode ();
printf("\033[?25l"); // hide cursor
/* int potion_sprite_width = 2; */
/* int potion_sprite_height = 2; */
/* char potion_sprite[2][2] = { { '/', '\\' }, */
/* /* { '\\', '/' } }; *\/ */
/* int potion_sprite_width = 4; */
/* int potion_sprite_height = 3; */
/* char potion_sprite[4][3] = { { 0x6C, 0x6B }, */
/* { 0x6D, 0x6A } }; */
int potion_sprite_width = 2;
int potion_sprite_height = 3;
char potion_sprite[3][2] = { { 'w', 'w' },
{ '|', '|' },
{ '/', '\\' } };
int width = 40;
int height = 20;
// player data
// 0 1 2
enum { vulnerable, invincible, dead };
int player_state = vulnerable;
int player_x = width / 2;
int player_y = height / 2;
// enemy data
int enemy_x = 1;
int enemy_y = 1;
enum { chasing, running, defeated };
int enemy_state = chasing;
// potion data
int potion_x = 29;
int potion_y = 11;
int random = 1;
enum { available, using, done };
int potion_state = available;
int potion_counter = 0;
while (1) {
printf("\x1b[2J");
printf("\x1b[H");
switch (potion_state) {
case available:
/* printf("\e(0"); */
for (int j = 0; j < potion_sprite_height; j++) {
printf("\033[%d;%dH", potion_y + j, potion_x);
for (int i = 0; i < potion_sprite_width; i++) {
putchar(potion_sprite[j][i]);
}
}
/* printf("\e(B\n"); */
/* printf("\033[%d;%dH", potion_y, potion_x); */
/* printf("/\\"); */
/* printf("\033[%d;%dH", potion_y + 1, potion_x); */
/* printf("\\/"); */
break;
case using:
// intentionally empty
break;
}
printf("\033[%d;%dH", enemy_y, enemy_x);
switch (enemy_state) {
case chasing: // case 0:
putchar('@');
break;
case running: // case 1:
putchar('@');
break;
case defeated: // case 2:
putchar('X');
}
printf("\033[%d;%dH", player_y, player_x);
switch (player_state) {
case vulnerable:
putchar('h');
break;
case invincible:
putchar('H');
break;
case dead:
printf(":(");
}
/* // debugging output */
/* printf("\033[%d;%dH", height + 1, 0); */
/* printf("player_y: %d\n", player_y); */
/* printf("player_x: %d\n", player_x); */
/* printf("enemy_y: %d\n", enemy_y); */
/* printf("enemy_x: %d\n", enemy_x); */
/* printf("potion_y: %d\n", potion_y); */
/* printf("potion_x: %d\n", potion_x); */
// read input
int direction_x = 0;
int direction_y = 0;
bool valid = true;
do {
char c = '\0';
scanf("%c", &c);
switch (c) {
case 'i':
direction_y = -2;
break;
case 'k':
direction_y = 2;
break;
case 'j':
direction_x = -2;
break;
case 'l':
direction_x = 2;
break;
default:
valid = false;
break;
}
} while (! valid);
switch(player_state) {
case vulnerable: // case 0:
player_x += direction_x;
player_y += direction_y;
break;
case invincible: // case 1:
player_x += direction_x;
player_y += direction_y;
if (enemy_x - 1 <= player_x && player_x <= enemy_x + 1 &&
enemy_y - 1 <= player_y && player_y <= enemy_y + 1) {
player_state = vulnerable;
enemy_state = defeated;
potion_state = done;
}
break;
}
switch (enemy_state) {
case chasing: // chasing
if (player_x > enemy_x) {
enemy_x += 1;
}
if (player_x < enemy_x) {
enemy_x -= 1;
}
if (player_y > enemy_y) {
enemy_y += 1;
}
if (player_y < enemy_y) {
enemy_y -= 1;
}
if ((player_x == enemy_x || player_x == enemy_x + 1) &&
(player_y == enemy_y || player_y == enemy_y + 1)) {
player_state = dead;
}
break;
case running:
if (player_x > enemy_x) {
enemy_x -= 1;
}
if (player_x < enemy_x) {
enemy_x += 1;
}
if (player_y > enemy_y) {
enemy_y -= 1;
}
if (player_y < enemy_y) {
enemy_y += 1;
}
if (enemy_x < 0) {
enemy_x = 0;
}
if (enemy_x > width) {
enemy_x = width;
}
if (enemy_y < 0) {
enemy_y = 0;
}
if (enemy_y > height) {
enemy_y = height;
}
break;
}
switch (potion_state) {
case available:
if ((player_x == potion_x || player_x == potion_x + 1) &&
(player_y == potion_y || player_y == potion_y + 1)) {
player_state = invincible;
enemy_state = running;
potion_state = using;
potion_counter = 10;
}
break;
case using:
potion_counter -= 1;
if (potion_counter == 1) {
player_state = vulnerable;
enemy_state = chasing;
potion_state = available;
random = (random * 97) % 65533;
potion_x = random % width;
random = (random * 97) % 65533;
potion_y = random % height;
}
break;
}
}
}
Background Arrays
Same idea as sprite arrays
txt -> c array generator python script
wget https://www.cs.ucf.edu/~gazzillo/teaching/cop3223hspring26/livedemos/full_game/background.py python3 background.py < background.array > background.c
Example: larger screen
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <stdio.h>
#include <stdbool.h>
#include "term.c"
int main(int argc, char **argv) {
set_input_mode ();
printf("\033[?25l"); // hide cursor
/* int potion_sprite_width = 2; */
/* int potion_sprite_height = 2; */
/* char potion_sprite[2][2] = { { '/', '\\' }, */
/* /* { '\\', '/' } }; *\/ */
/* int potion_sprite_width = 4; */
/* int potion_sprite_height = 3; */
/* char potion_sprite[4][3] = { { 0x6C, 0x6B }, */
/* { 0x6D, 0x6A } }; */
int potion_sprite_width = 2;
int potion_sprite_height = 2;
char potion_sprite[2][2] = { { 'w', 'w' },
{ '\\', '/' } };
int width = 40;
int height = 20;
int background_rows = 21;
int background_cols = 41;
char background[21][41] = {
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',},
{ ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '_', '_', '-', '-', '-', '-', '/', ' ', ' ', ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', ' ', '_', '-', '-', '/',},
{ ' ', ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '/', ' ', ' ','\\', '-', '-', '/', ' ', ' ', ' ', ' ', ' ','\\', '_', '_', '/','\\', '-', '-', '_', '_', '/','\\', '/', ' ','\\', '/', ' ',},
{ ' ', ' ', ' ','\\', '_', '_', '/','\\', '-', '-', '_', '_', '/', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '_', '/','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '/', '-', '-', '-','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '|', '-', '-', '-', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '_', '_', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', '_', '_', '_', '_', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', '/', '-', '-', '-', '-','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ','\\', '-', '-', '-', '-', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', '|', '_', '_', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '_', '/','\\', ' ', ' ', ' ', ' ', ' ', '/','\\', '_', ' ', ' ', ' ', ' ',},
{ ' ', '_', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '/', '-', '-', '-','\\', '_', '_', '_', '/', '-', '-', '-','\\', ' ', ' ', '_',},
{ ' ', '-','\\', ' ', ' ', ' ', ' ', '_', '_', ' ', ' ', '/','\\', ' ', ' ', ' ', ' ', ' ', ' ', '/','\\', ' ', ' ', '_', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-','\\', '/', '-',},
{ ' ', '-', '-','\\', ' ', ' ', '/', '-', '-','\\', '/', '-', '-','\\', ' ', ' ', ' ', ' ', '/', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ ' ', '-', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-','\\', ' ', ' ', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ ' ', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ ' ', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',},
};
// player data
// 0 1 2
enum { vulnerable, invincible, dead };
int player_state = vulnerable;
int player_x = width / 2;
int player_y = height / 2;
// enemy data
int enemy_x = 2;
int enemy_y = 2;
enum { chasing, running, defeated };
int enemy_state = chasing;
// potion data
int potion_x = 29;
int potion_y = 11;
int random = 1;
enum { available, using, done };
int potion_state = available;
int potion_counter = 0;
while (1) {
printf("\x1b[2J");
printf("\x1b[H");
// draw background
background_rows = 21;
background_cols = 41;
background[21][41];
for (int j = 1; j < background_rows; j++) {
printf("\033[%d;%dH", j, 1);
for (int i = 1; i < background_cols; i++) {
putchar(background[j][i]);
}
}
switch (potion_state) {
case available:
/* printf("\e(0"); */
for (int j = 0; j < potion_sprite_height; j++) {
printf("\033[%d;%dH", potion_y + j, potion_x);
for (int i = 0; i < potion_sprite_width; i++) {
putchar(potion_sprite[j][i]);
}
}
/* printf("\e(B\n"); */
/* printf("\033[%d;%dH", potion_y, potion_x); */
/* printf("/\\"); */
/* printf("\033[%d;%dH", potion_y + 1, potion_x); */
/* printf("\\/"); */
break;
case using:
// intentionally empty
break;
}
printf("\033[%d;%dH", enemy_y, enemy_x);
switch (enemy_state) {
case chasing: // case 0:
putchar('@');
break;
case running: // case 1:
putchar('@');
break;
case defeated: // case 2:
putchar('X');
}
printf("\033[%d;%dH", player_y, player_x);
switch (player_state) {
case vulnerable:
putchar('h');
break;
case invincible:
putchar('H');
break;
case dead:
printf(":(");
}
/* // debugging output */
/* printf("\033[%d;%dH", height + 1, 0); */
/* printf("player_y: %d\n", player_y); */
/* printf("player_x: %d\n", player_x); */
/* printf("enemy_y: %d\n", enemy_y); */
/* printf("enemy_x: %d\n", enemy_x); */
/* printf("potion_y: %d\n", potion_y); */
/* printf("potion_x: %d\n", potion_x); */
// read input
int direction_x = 0;
int direction_y = 0;
bool valid = true;
do {
char c = '\0';
scanf("%c", &c);
switch (c) {
case 'i':
direction_y = -2;
break;
case 'k':
direction_y = 2;
break;
case 'j':
direction_x = -2;
break;
case 'l':
direction_x = 2;
break;
default:
valid = false;
break;
}
} while (! valid);
switch(player_state) {
case vulnerable: // case 0:
player_x += direction_x;
player_y += direction_y;
break;
case invincible: // case 1:
player_x += direction_x;
player_y += direction_y;
if (enemy_x - 1 <= player_x && player_x <= enemy_x + 1 &&
enemy_y - 1 <= player_y && player_y <= enemy_y + 1) {
player_state = vulnerable;
enemy_state = defeated;
potion_state = done;
}
break;
}
switch (enemy_state) {
case chasing: // chasing
if (player_x > enemy_x) {
enemy_x += 1;
}
if (player_x < enemy_x) {
enemy_x -= 1;
}
if (player_y > enemy_y) {
enemy_y += 1;
}
if (player_y < enemy_y) {
enemy_y -= 1;
}
if ((player_x == enemy_x || player_x == enemy_x + 1) &&
(player_y == enemy_y || player_y == enemy_y + 1)) {
player_state = dead;
}
break;
case running:
if (player_x > enemy_x) {
enemy_x -= 1;
}
if (player_x < enemy_x) {
enemy_x += 1;
}
if (player_y > enemy_y) {
enemy_y -= 1;
}
if (player_y < enemy_y) {
enemy_y += 1;
}
if (enemy_x < 0) {
enemy_x = 0;
}
if (enemy_x > width) {
enemy_x = width;
}
if (enemy_y < 0) {
enemy_y = 0;
}
if (enemy_y > height) {
enemy_y = height;
}
break;
}
switch (potion_state) {
case available:
if ((player_x == potion_x || player_x == potion_x + 1) &&
(player_y == potion_y || player_y == potion_y + 1)) {
player_state = invincible;
enemy_state = running;
potion_state = using;
potion_counter = 10;
}
break;
case using:
potion_counter -= 1;
if (potion_counter == 1) {
player_state = vulnerable;
enemy_state = chasing;
potion_state = available;
random = (random * 97) % 65533;
potion_x = random % width;
random = (random * 97) % 65533;
potion_y = random % height;
}
break;
}
}
}
figlet
Use figlet to generate ascii art text banners.
See ls /usr/share/figlet for the names of fonts to pass to -f, e.g.,
figlet -f emboss hello
ASCII art to C array
wget https://www.cs.ucf.edu/~gazzillo/teaching/cop3223hspring26/livedemos/full_game/background.py python3 background.py < background.array > background.c
Side scrolling
Example: background scrolling
scroll_animate.c
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("\033[?25l"); // hide cursor
#include "background.c"
int scroll_x_offset = 0;
int scroll_y_offset = 0;
while (1) {
printf("\x1b[2J");
printf("\x1b[H");
// draw background
background_rows = 21;
background_cols = 41;
background[21][41];
for (int j = 1; j < background_rows; j++) {
printf("\033[%d;%dH", j, 1);
for (int i = 1; i < background_cols; i++) {
// converting the character array position to an infinitely-sized screen
int screen_y = (scroll_y_offset + j);
int screen_x = (scroll_x_offset + i);
// convert the screen position into a viewbox
int viewbox_y = screen_y % background_rows;
int viewbox_x = screen_x % background_cols;
putchar(background[viewbox_y][viewbox_x]);
}
}
scroll_x_offset = (scroll_x_offset + 1) % background_cols;
fflush(stdout);
usleep(80 * 1000);
}
}
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("\033[?25l"); // hide cursor
#include "background.c"
int scroll_x_offset = 0;
int scroll_y_offset = 0;
while (1) {
printf("\x1b[2J");
printf("\x1b[H");
// draw background
background_rows = 21;
background_cols = 41;
background[21][41];
for (int j = 1; j < background_rows; j++) {
printf("\033[%d;%dH", j, 1);
for (int i = 1; i < background_cols; i++) {
putchar(background[(scroll_y_offset + j) % background_rows][(scroll_x_offset + i) % background_cols]);
}
}
scroll_x_offset = (scroll_x_offset + 1) % background_cols;
fflush(stdout);
usleep(80 * 1000);
}
}
background.c
int background_rows = 21;
int background_cols = 41;
char background[21][41] = {
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',},
{ ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '_', '_', '-', '-', '-', '-', '/', ' ', ' ', ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', ' ', '_', '-', '-', '/',},
{ ' ', ' ','\\', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '/', ' ', ' ','\\', '-', '-', '/', ' ', ' ', ' ', ' ', ' ','\\', '_', '_', '/','\\', '-', '-', '_', '_', '/','\\', '/', ' ','\\', '/', ' ',},
{ ' ', ' ', ' ','\\', '_', '_', '/','\\', '-', '-', '_', '_', '/', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '_', '/','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '/', '-', '-', '-','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '|', '-', '-', '-', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','\\', '_', '_', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', '_', '_', '_', '_', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', '/', '-', '-', '-', '-','\\', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ','\\', '-', '-', '-', '-', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', '|', '_', '_', '/', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',},
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '_', '/','\\', ' ', ' ', ' ', ' ', ' ', '/','\\', '_', ' ', ' ', ' ', ' ',},
{ '_', '_', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '/', '-', '-', '-','\\', '_', '_', '_', '/', '-', '-', '-','\\', ' ', ' ', '_',},
{ '-', '-','\\', ' ', ' ', ' ', ' ', '_', '_', ' ', ' ', '/','\\', ' ', ' ', ' ', ' ', ' ', ' ', '/','\\', ' ', ' ', '_', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-','\\', '/', '-',},
{ '-', '-', '-','\\', ' ', ' ', '/', '-', '-','\\', '/', '-', '-','\\', ' ', ' ', ' ', ' ', '/', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ '-', '-', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-','\\', ' ', ' ', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-','\\', '/', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',},
{ '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',},
};
Asynchronous input
| Function | Description |
|---|---|
kbhit() |
Wait for keyboard input |
getch() |
Read the input |
Waiting for input
Loop while there is no keyboard input
do {
// draw screen, game logic, etc.
} while (! kbhit());
Read the input
Read the character
c = getch();
Simple illustration
char c = 'a';
while (1) {
do {
// print a character until there is keyboard input
putchar(c);
fflush(stdout);
usleep(80 * 1000);
} while (! kbhit()); // until keyboard input
// stop looping and read the character
c = getch();
} // continue looping and printing the character
Full example
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
#include <stdio.h>
#include <stdbool.h>
#include "term.c"
int main(int argc, char **argv) {
set_input_mode ();
char c = 'a';
while (1) {
do {
putchar(c);
fflush(stdout);
usleep(80 * 1000);
} while (! kbhit());
c = getch();
}
}
Example: side scroller
term.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <termios.h>
// https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Noncanon-Example
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
// https://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input
bool kbhit() {
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
if (select(1, &fds, NULL, NULL, &tv) == 1) {
return true;
} else {
return false;
}
}
int getch() {
int r;
unsigned char c;
do {
if ((r = read(0, &c, sizeof(c))) < 0) {
return '\0'; // null character on error
}
} while (r < 1); // ensures read returns one character
return c;
}
Sprite collision detection
chase.c
Background collision detection
Gravity
gravity_bounce.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
int height = 20;
int width = 40;
float y = 5;
float vy = 0;
float a = .05;
float x = 0;
float vx = .6;
float loss = .98;
while (1) {
printf("\033[2J");
printf("\033[H");
/* printf("\033[%d;%dH", (int)y, (int)x); */
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
if ((i == (int) x) && (j == (int) y)) {
putchar('o');
} else {
putchar(' ');
}
}
putchar('|');
putchar('\n');
}
for (int i = 0; i < width; i++) {
putchar('^');
}
putchar('\n');
/* printf("\033[%d;%dH", (int)y, (int)x); */
/* putchar('o'); */
if ((0 <= (x + vx)) && ((x + vx) < width)) {
vx = vx;
} else {
vx = vx * -1;
}
x = x + vx;
vy = vy + a;
y = y + vy;
if ((int) y > height) {
vy = -1 * vy * loss;
y = y + vy;
}
printf("\033[%d;%dH", height + 2, 0);
printf("y: %f\n", y);
printf("x: %f\n", x);
printf("vy: %f\n", vy);
printf("vx: %f\n", vx);
usleep(25 * 1000);
}
return 0;
}
Random numbers
random.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
/* srand(2); */
int seed = arc4random();
printf("seed = %d\n", seed);
srand(seed);
int r = rand();
printf("r = %d\n", r);
int low = 10; // inclusive
int high = 20; // exclusive
int r_between = low + (r % (high - low));
printf("r_between [%d, %d) = %d\n", low, high, r_between);
}