Last active
June 12, 2019 07:33
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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