Skip to content

Instantly share code, notes, and snippets.

@eevee
Last active June 12, 2019 07:33
Show Gist options
  • Save eevee/aa71799e7ec0a4981ee13ea8185b854c to your computer and use it in GitHub Desktop.
Save eevee/aa71799e7ec0a4981ee13ea8185b854c to your computer and use it in GitHub Desktop.
Hex: a two-player video game I wrote when I was sixteen (requires Allegro 4.2)
// Hex!
// By Eevee
// Started Feb 10, 2003
/* THINGS TO DO:
* - make it check for a win DONE!
* - add an option for changing board size NO
* - player stats/color DONE!
* - make menu less crappy DONE!
* - save options somewhere NO
* - make player color outline thingy DONE!
* - cheats? DONE!
*/
#include <allegro.h>
#include <math.h>
#include <randomc.h>
#include <time.h>
#include <randomc/mersenne.cpp>
#define myfor(a, b, c) for(a = b; a < c; a++)
#define inc(a, b, c) a+=b+c;a%=c
#define BOARD_SIZE 10 // change this line to make the board bigger or smaller
#define veefresh() make_outline(hexboard);draw_sprite(hexboard,blankboard,0,0);fillboard(hexboard,board);update_player_colors(playerstat,marker,curplayer);draw_sprite(screen,hexboard,offsetx,offsety);blit(menu, screen, 0, 0, 0, 0, 160, 480);
const float pi = 3.141592653589793238462;
int hexedge = 16;
int hexw = (int)(hexedge * sqrt(3));
int hexh = hexedge * 2;
int fonthgt;
bool broken_win = false;
int COLOR_HEX = 15; // color of the hex board's lines
int COLOR_PLAYER[] = { 2, 4 }; // starting colors for players 1 and 2
int check_for_win(int [BOARD_SIZE][BOARD_SIZE]);
void check_branch(int [BOARD_SIZE][BOARD_SIZE], int, int);
void update_player_colors(BITMAP *[], BITMAP *, int);
int toggle_fullscreen(void);
void fillboard(BITMAP *, int [BOARD_SIZE][BOARD_SIZE]);
void make_outline(BITMAP *);
void colorinc(int, int);
BITMAP *wingen(int);
TRandomMersenne randgen(time(0));
int main(int argc, char **argv) {
// Initialization stuff
if (allegro_init()) { return 1; }
install_keyboard();
install_timer();
if (toggle_fullscreen()) {
allegro_message("Something screwed up: '%s'\n", allegro_error);
return 1;
}
set_window_title("Hex v1.02");
int i, j, curplayer = 0, r = 0, c = 0, curkey, winner;
int cheats[2] = { 0, 0 }, moves = 0, cheatm = 0;
bool cheat = false;
fonthgt = text_height(font); int fontwdt = text_length(font, "A");
int offsetx = 400 - ((3 * hexw - 1) * BOARD_SIZE / 2 + 1) / 2 - 2,
offsety = 240 - ((hexh * 3 / 4) * BOARD_SIZE + hexh / 4 + 1) / 2 - 2;
int board[BOARD_SIZE][BOARD_SIZE];
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE) board[i][j] = 0;
BITMAP *menu = create_bitmap(160, 480);
// Create the 'menu'
clear_bitmap(menu);
rect(menu, 0, 0, 159, 479, 15);
rect(menu, 1, 1, 158, 478, 8);
rect(menu, 2, 2, 157, 477, 8);
floodfill(menu, 3, 3, 7);
textout_ex(menu, font, "Arrows to move", 20, 20, 0, -1);
textout_ex(menu, font, "Enter or Space", 20, 40, 0, -1);
textout_ex(menu, font, " to put piece", 20, 60, 0, -1);
textout_ex(menu, font, "[] changes color", 20, 80, 0, -1);
textout_ex(menu, font, "F2 for new game", 20, 100, 0, -1);
textout_ex(menu, font, "Esc to quit", 20, 120, 0, -1);
textout_ex(menu, font, "D for debug", 20, 160, 0, -1);
textout_ex(menu, font, "R to refresh", 20, 180, 0, -1);
textout_ex(menu, font, "S to scramble", 20, 200, 0, -1);
textout_centre_ex(menu, font, "-=[ Hex 1.02 ]=-", 80, 440 - fonthgt, 9, -1);
textout_centre_ex(menu, font, "-=[ By Eevee ]=-", 80, 460 - fonthgt, 9, -1);
blit(menu, screen, 0, 0, 0, 0, 160, 480);
// Create the bitmap of a hexagon
BITMAP *hexpiece = create_bitmap(hexw + 1, hexh + 1);
clear_bitmap(hexpiece);
line(hexpiece, 0, hexh / 4, hexw / 2, 0, COLOR_HEX);
line(hexpiece, 0, hexh * 3 / 4, hexw / 2, hexh, COLOR_HEX);
line(hexpiece, 0, hexh / 4, 0, hexh * 3 / 4, COLOR_HEX);
line(hexpiece, hexw, hexh / 4, hexw / 2+1, 0, COLOR_HEX);
line(hexpiece, hexw, hexh * 3 / 4, hexw / 2+1, hexh, COLOR_HEX);
line(hexpiece, hexw, hexh / 4, hexw, hexh * 3 / 4, COLOR_HEX);
floodfill(hexpiece, hexw / 2, hexh / 2, 8);
// Create the board and fill it with hexagons
BITMAP *hexboard = create_bitmap((3 * hexw - 1) * BOARD_SIZE / 2 + 5,
(hexh * 3 / 4) * BOARD_SIZE + hexh / 4 + 5);
BITMAP *blankboard = create_bitmap((3 * hexw - 1) * BOARD_SIZE / 2 + 5,
(hexh * 3 / 4) * BOARD_SIZE + hexh / 4 + 5);
clear_bitmap(blankboard);
clear_bitmap(hexboard);
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE)
draw_sprite(blankboard, hexpiece, hexw * (2 * i + j) / 2 + 2, hexh * j *
3 / 4 + 2);
make_outline(hexboard);
draw_sprite(hexboard, blankboard, 0, 0);
fillboard(hexboard, board);
// Pretty star... became a arrow.
BITMAP *marker = create_bitmap(fonthgt * 6, fonthgt * 6);
clear_bitmap(marker);
line(marker, fonthgt, fonthgt * 4, fonthgt, fonthgt * 2, 15);
line(marker, fonthgt, fonthgt * 2, fonthgt * 3, fonthgt * 2, 15);
line(marker, fonthgt * 3, fonthgt * 2, fonthgt * 3, fonthgt, 15);
line(marker, fonthgt * 3, fonthgt, fonthgt * 5, fonthgt * 3, 15);
line(marker, fonthgt * 5, fonthgt * 3, fonthgt * 3, fonthgt * 5, 15);
line(marker, fonthgt * 3, fonthgt * 5, fonthgt * 3, fonthgt * 4, 15);
line(marker, fonthgt * 3, fonthgt * 4, fonthgt, fonthgt * 4, 15);
floodfill(marker, fonthgt * 3, fonthgt * 3, 15);
draw_sprite(screen, marker, fonthgt * 2 + 160, fonthgt * 2);
// Create the two player status area thingies
int psw = 480 - 4 * fonthgt, psh = 6 * fonthgt;
BITMAP *playerstat[2] = {create_bitmap(psw, psh), create_bitmap(psw, psh)};
clear_bitmap(playerstat[0]); clear_bitmap(playerstat[1]);
textout_ex(playerstat[0], font, "Player 1", fonthgt * 7, fonthgt, 15, -1);
textout_ex(playerstat[0], font, "(left to right)", fonthgt * 7, fonthgt * 4,
15, -1);
textout_ex(playerstat[1], font, "Player 2", fonthgt * 7, fonthgt, 15, -1);
textout_ex(playerstat[1], font, "(top to bottom)", fonthgt * 7, fonthgt * 4,
15, -1);
update_player_colors(playerstat, marker, curplayer);
// Main keygetting loop
while (true) {
draw_sprite(screen, hexboard, offsetx, offsety);
textout_centre_ex(screen, font, "*", hexw * (2 * c + r + 1) / 2 +
offsetx + 3, (hexh * (r * 3 + 2) - fonthgt * 2) / 4 + offsety + 3,
7, -1);
curkey = readkey() >> 8;
switch(curkey) {
case KEY_OPENBRACE: colorinc(curplayer, 1); veefresh(); break;
case KEY_CLOSEBRACE: colorinc(curplayer, -1); veefresh(); break;
case KEY_UP: inc(r, -1, BOARD_SIZE); break;
case KEY_DOWN: inc(r, 1, BOARD_SIZE); break;
case KEY_LEFT: inc(c, -1, BOARD_SIZE); break;
case KEY_RIGHT: inc(c, 1, BOARD_SIZE); break;
case KEY_D: broken_win = !broken_win; break;
case KEY_S: myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE)
if (board[i][j]) board[i][j] = randgen.IRandom(1, 2);
veefresh(); break;
case KEY_ENTER: if (key[KEY_ALT] || key[KEY_ALTGR]) {
toggle_fullscreen(); veefresh(); break;
}
case KEY_SPACE: if (board[r][c] == 0) {
board[r][c] = curplayer + 1;
floodfill(hexboard, hexw * (2 * c + r + 1) / 2, hexh * (r *
3 + 2) / 4, COLOR_PLAYER[curplayer]);
curplayer = 1 - curplayer; moves++;
if (cheat && (moves - cheatm) % 3 == 0) {
int r = -1, c = -1;
do {
r = randgen.IRandom(0, 9);
c = randgen.IRandom(0, 9);
} while (board[r][c] == 0);
board[r][c] = 0;
veefresh();
}
}
if (winner = check_for_win(board)) {
draw_sprite(screen, hexboard, offsetx, offsety);
int tw = text_length(font, "Player 1 wins!");
blit(wingen(winner), screen, 0, 0, 640 - fonthgt * 2 - tw * 2,
fonthgt * 4, tw * 2, fonthgt * 2);
while (true) {
curkey = readkey() >> 8;
if (curkey == KEY_ESC) return 0;
if (curkey == KEY_F2) break;
}
} else {
update_player_colors(playerstat, marker, curplayer);
break;
}
case KEY_F2:
r = c = curplayer = cheats[0] = cheats[1] = moves = cheatm = 0;
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE) board[i][j] = 0;
veefresh(); break;
case KEY_ESC: return 0; break;
case KEY_R: veefresh(); break;
case KEY_V: if (key[KEY_LCONTROL] && key[KEY_RSHIFT]) {
cheat = true; cheatm = moves; }
break;
case KEY_8: if (key[KEY_RWIN] && key[KEY_ALT] && cheat) {
cheats[curplayer]++; int m = 2 - curplayer, n = 0;
if (cheats[curplayer] > 1) { m = curplayer + 1; n = 2 - curplayer; }
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE)
if (board[i][j] == m) board[i][j] = n;
veefresh();
}
break;
}
clear_keybuf();
}
}
// Check the board for a win by recursively propagating negative numbers from
// one side and checking for them on the other
int check_for_win(int board[BOARD_SIZE][BOARD_SIZE]) {
int i, j, copy[BOARD_SIZE][BOARD_SIZE];
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE) copy[i][j] = board[i][j];
// Player 1 (left to right)
myfor(i, 0, BOARD_SIZE) if (copy[i][0] == 1) check_branch(copy, i, 0);
myfor(i, 0, BOARD_SIZE) if (copy[i][BOARD_SIZE-1] == -1) return 1;
// Player 2 (top to bottom)
myfor(i, 0, BOARD_SIZE) if (copy[0][i] == 2) check_branch(copy, 0, i);
myfor(i, 0, BOARD_SIZE) if (copy[BOARD_SIZE-1][i] == -2) return 2;
if (broken_win) myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE) textprintf_ex(screen, font, 15 * j, 15 * i, 15, 0, "%d", copy[i][j]);
return 0;
}
// Propagate the negatives to each adjacent matching tile
void check_branch(int board[BOARD_SIZE][BOARD_SIZE], int r, int c) {
int p = board[r][c];
board[r][c] *= -1;
// r, c + 1 | r, c - 1 | r - 1, c | r + 1, c | r - 1, c + 1 | r + 1, c - 1
if (c < BOARD_SIZE - 1 && board[r][c+1] == p) check_branch(board, r, c+1);
if (c > 0 && board[r][c-1] == p) check_branch(board, r, c-1);
if (r < BOARD_SIZE - 1 && board[r+1][c] == p) check_branch(board, r+1, c);
if (r > 0 && board[r-1][c] == p) check_branch(board, r-1, c);
if (r > 0 && c < BOARD_SIZE - 1 && board[r-1][c+1] == p)
check_branch(board, r-1, c+1);
if (r < BOARD_SIZE - 1 && c > 0 && board[r+1][c-1] == p)
check_branch(board, r+1, c-1);
}
// Redraw the color blocks and active player marker
void update_player_colors(BITMAP *playerstat[], BITMAP *marker, int curplayer) {
rectfill(playerstat[0], 0, 0, fonthgt * 6, fonthgt * 6, COLOR_PLAYER[0]);
rectfill(playerstat[1], 0, 0, fonthgt * 6, fonthgt * 6, COLOR_PLAYER[1]);
blit(playerstat[0], screen, 0, 0, fonthgt * 2 + 160, fonthgt * 2, 480 -
fonthgt * 4, fonthgt * 6);
blit(playerstat[1], screen, 0, 0, fonthgt * 2 + 160, 480 - fonthgt * 8, 480
- fonthgt * 4, fonthgt * 6);
floodfill(marker, fonthgt * 3, fonthgt * 3, (COLOR_PLAYER[curplayer] < 9) ?
15 : 8);
draw_sprite(screen, marker, fonthgt * 2 + 160, curplayer ? 480 - fonthgt * 8
: fonthgt * 2);
}
// Swap fullscreen and windowed mode
int toggle_fullscreen() {
static int mode = 0; // 0 for windowed, 1 for full
int r;
mode = 1 - mode;
r = set_gfx_mode(mode ? GFX_AUTODETECT : GFX_AUTODETECT_WINDOWED, 640, 480,
0, 0);
return r;
}
void colorinc(int cp, int dir) {
do { inc(COLOR_PLAYER[cp], dir, 16); } while (COLOR_PLAYER[cp] == 8 ||
COLOR_PLAYER[cp] == 7 || COLOR_PLAYER[cp] == 0 || COLOR_PLAYER[cp] ==
COLOR_PLAYER[1 - cp]);
}
void fillboard(BITMAP *hexboard, int board[BOARD_SIZE][BOARD_SIZE]) {
int i, j;
myfor(i, 0, BOARD_SIZE) myfor(j, 0, BOARD_SIZE) if (board[i][j])
floodfill(hexboard, hexw * (j + 1 + i / 2), hexh * (i + 1) * 3 / 4,
COLOR_PLAYER[board[i][j]-1]);
}
void make_outline(BITMAP *board) {
int i, j;
int b = hexw * (BOARD_SIZE - 1) / 2, h = hexh * 3 * BOARD_SIZE / 4 + 3;
// top
myfor(j, 0, 2) myfor(i, 0, BOARD_SIZE) {
line(board, hexw * i + 2, hexh / 4 + j, hexw * (2 * i + 1) / 2 + 2, j,
COLOR_PLAYER[1]);
line(board, hexw * (2 * i + 1) / 2 + 3, j, hexw * (i + 1) + 2, hexh / 4
+ j, COLOR_PLAYER[1]);
}
// bottom
myfor(j, 0, 2) myfor(i, 0, BOARD_SIZE) {
line(board, hexw * i + b + 2, h + j, hexw * (2 * i + 1) / 2 + b + 2, h +
j + hexh / 4, COLOR_PLAYER[1]);
line(board, hexw * (2 * i + 1) / 2 + b + 3, h + j + hexh / 4, hexw * (i
+ 1) + b + 2, h + j, COLOR_PLAYER[1]);
}
// left
myfor(j, 0, 2) myfor(i, 0, BOARD_SIZE) {
vline(board, hexw * i / 2 + j, hexh * (3 * i + 1) / 4 + 2, hexh *
(i + 1) * 3 / 4 + 2, COLOR_PLAYER[0]);
if (i != BOARD_SIZE - 1) line(board, hexw * i / 2 + 1, hexh * (i + 1) *
3 / 4 + 2 + j, hexw * (i + 1) / 2 + 1, hexh * (i * 3 + 4) / 4 + 2 + j,
COLOR_PLAYER[0]);
}
// right
myfor(j, 0, 2) myfor(i, 0, BOARD_SIZE) {
vline(board, hexw * i / 2 + j + BOARD_SIZE * hexw + 3, hexh * (3 * i +
1) / 4 + 2, hexh * (i + 1) * 3 / 4 + 2, COLOR_PLAYER[0]);
if (i != BOARD_SIZE - 1) line(board, hexw * i / 2 + j + BOARD_SIZE *
hexw + 3, hexh * (i + 1) * 3 / 4 + 1 + j, hexw * (i + 1) / 2 + j +
BOARD_SIZE * hexw + 3, hexh * (i * 3 + 4) / 4 + 1 + j, COLOR_PLAYER[0]);
}
}
BITMAP *wingen(int winner) {
// Winning move design thingy
int tw = text_length(font, "Player 1 wins!");
BITMAP *twinbmp = create_bitmap( tw, fonthgt );
BITMAP *winbmp = create_bitmap( tw * 2, fonthgt * 2 );
clear_bitmap(twinbmp); clear_bitmap(winbmp);
textprintf_ex(twinbmp, font, 0, 0, COLOR_PLAYER[winner-1], -1,
"Player %d wins!", winner);
stretch_blit(twinbmp, winbmp, 0, 0, tw, fonthgt, 0, 0, tw * 2, fonthgt * 2);
return winbmp;
}
END_OF_MAIN();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment