First release of 2048
This commit is contained in:
commit
50d1f8b5f7
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
* 2048, by afrangry
|
||||
* Copyright (c) 2023 Afrangry Hill <donotcalllist@privacyrequired.com>
|
||||
* This work is free. You can redistribute it and/or modify it under the
|
||||
* terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||
* as published by Sam Hocevar. See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define N 4
|
||||
typedef struct arr {
|
||||
int x; /* position on x-axis from left to right */
|
||||
int y; /* position on y-axis from top to bottom */
|
||||
} ARR; /* position in a 2D map with x and y value */
|
||||
|
||||
static int hiscore = 0; /* highest score of user */
|
||||
static int hinum = 0; /* highest number of user */
|
||||
static int score = 0; /* score of current game */
|
||||
static int number = 0; /* highest number of current game */
|
||||
|
||||
static void map_init(int *map);
|
||||
static int *arr2ptr(int *start, ARR arr);
|
||||
static ARR ptr2arr(int *start, int *ptr);
|
||||
static void cpymap(int *dst, int *ptr);
|
||||
static void trans(int *map, int time);
|
||||
static void prtwelcome(void);
|
||||
static int moveup1(int *map, int *ptr);
|
||||
static void asgn_if_src_true(int *dst, int src);
|
||||
static int moveup(int *map);
|
||||
static int eat1(int *map, int *ptr);
|
||||
static int eat(int *map);
|
||||
static int upeat(int *map);
|
||||
static void puttb(void);
|
||||
static void putm(void);
|
||||
static void putnumline(int *map, int line);
|
||||
static void prt(int *map);
|
||||
static int genrnd(int *map);
|
||||
static int calhinum(int *map);
|
||||
static int same_as_follower(int *map, int *ptr);
|
||||
static int isover(int *map);
|
||||
static int input_to_trans(char input);
|
||||
static void turn(int *map);
|
||||
static void game(int *map);
|
||||
static void gg(int *map);
|
||||
|
||||
/*
|
||||
* fill map with 0
|
||||
*/
|
||||
static void
|
||||
map_init(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int *curmap = map; /* current pointer in map, used for iteration */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
for (j = 0; j < N; j++) {
|
||||
*curmap = 0;
|
||||
curmap++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* convert arr type to pointer in array
|
||||
*/
|
||||
static int * /* return pointer to given arr */
|
||||
arr2ptr(int *start, /* pointer to first element of array */
|
||||
ARR arr /* arr to be converted */
|
||||
)
|
||||
{
|
||||
return start + arr.x + N * arr.y;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert pointer in array to arr type
|
||||
*/
|
||||
static ARR /* return arr of given pointer */
|
||||
ptr2arr(int *start, /* pointer to first element of array */
|
||||
int *ptr /* pointer to be converted */
|
||||
)
|
||||
{
|
||||
ARR arr = { 0, 0 }; /* arr of given tile */
|
||||
|
||||
arr.x = (int)(ptr - start) % N;
|
||||
arr.y = (int)(ptr - start) / N;
|
||||
return arr;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy source map to destination map
|
||||
*/
|
||||
static void
|
||||
cpymap(int *dst, /* destination map */
|
||||
int *src /* source map */
|
||||
)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
*dst = *src;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rotate the map clockwise for a given time, persumes square map
|
||||
*/
|
||||
static void
|
||||
trans(int *map, /* pointer to first element of map */
|
||||
int time /* rotate given time */
|
||||
)
|
||||
{
|
||||
int tmp[N][N] = { 0 }; /* temporary map */
|
||||
int *ptrtmp = &tmp[0][0]; /* pointer to temporary map */
|
||||
int *curmap = map; /* current pointer to temporary map, iteration */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
if (time == 0)
|
||||
return;
|
||||
map_init(ptrtmp);
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
ARR maparr =
|
||||
ptr2arr(map, curmap); /* current arr of map */
|
||||
ARR tmparr = { 0,
|
||||
0 }; /* current arr of temporary map */
|
||||
|
||||
tmparr.x = N - maparr.y - 1;
|
||||
tmparr.y = maparr.x;
|
||||
*arr2ptr(ptrtmp, tmparr) = *curmap;
|
||||
curmap++;
|
||||
}
|
||||
}
|
||||
cpymap(map, ptrtmp);
|
||||
trans(map, time - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* print welcome screen
|
||||
*/
|
||||
static void
|
||||
prtwelcome(void)
|
||||
{
|
||||
puts("-------------------------");
|
||||
puts("| 2048 |");
|
||||
puts("|-----------------------|");
|
||||
printf("| High Score: %8d |\n", hiscore);
|
||||
printf("| High Number: %8d |\n", hinum);
|
||||
puts("|-----------------------|");
|
||||
puts("| w = Up s = Down |");
|
||||
puts("| a = Left d = Right |");
|
||||
puts("-------------------------");
|
||||
puts("Press space to continue... ");
|
||||
}
|
||||
|
||||
/*
|
||||
* move given tile up until border or another non-empty tile
|
||||
*/
|
||||
static int /* return 1 if change is made, 0 otherwise */
|
||||
moveup1(int *map, /* pointer to first element of map */
|
||||
int *ptr /* pointer to tile to be moved */
|
||||
)
|
||||
{
|
||||
ARR curarr = ptr2arr(map, ptr); /* arr of ptr */
|
||||
ARR tmp = { 0, 0 }; /* temporary arr for tile up 1 of curarr */
|
||||
int *ptrtmp = NULL; /* pointer to tile up 1 of curarr */
|
||||
|
||||
if (curarr.y == 0)
|
||||
return 0;
|
||||
if (*ptr == 0)
|
||||
return 0;
|
||||
tmp.x = curarr.x;
|
||||
tmp.y = curarr.y - 1;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
if (*ptrtmp > 0)
|
||||
return 0;
|
||||
*ptrtmp = *ptr;
|
||||
*ptr = 0;
|
||||
moveup1(map, ptrtmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* assign src to dst if src is true, otherwise do nothing
|
||||
*/
|
||||
static void
|
||||
asgn_if_src_true(int *dst, /* pointer to destination to be assigned */
|
||||
int src /* source */
|
||||
)
|
||||
{
|
||||
if (src)
|
||||
*dst = src;
|
||||
}
|
||||
|
||||
/*
|
||||
* recursively move all tiles up until border or non-empty tile
|
||||
*/
|
||||
static int /* return 1 if change is made, 0 otherwise */
|
||||
moveup(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int flag = 0; /* 1 if change is made, 0 otherwise */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
ARR tmp = { 0,
|
||||
0 }; /* temporary arr for current tile */
|
||||
int *ptrtmp = NULL; /* pointer to current tile */
|
||||
|
||||
tmp.x = i;
|
||||
tmp.y = j;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
asgn_if_src_true(&flag, moveup1(map, ptrtmp));
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* if tile up 1 of given tile is the same as given tile,
|
||||
* double tile up 1 and clear given tile
|
||||
*/
|
||||
static int /* return 1 if change is made, 0 otherwise */
|
||||
eat1(int *map, /* pointer to first element of map */
|
||||
int *ptr /* pointer to tile to eat up 1 */
|
||||
)
|
||||
{
|
||||
ARR curarr = ptr2arr(map, ptr); /* arr of ptr */
|
||||
int *ptrcur = ptr; /* ptr, redundant */
|
||||
ARR uparr = { 0, 0 }; /* arr of tile up 1 of curarr */
|
||||
int *ptrup = NULL; /* pointer tile up 1 of curarr */
|
||||
|
||||
if (*ptr == 0)
|
||||
return 0;
|
||||
uparr.x = curarr.x;
|
||||
uparr.y = curarr.y - 1;
|
||||
ptrup = arr2ptr(map, uparr);
|
||||
if (*ptrcur != *ptrup)
|
||||
return 0;
|
||||
*ptrup += *ptrcur;
|
||||
*ptrcur = 0;
|
||||
score += *ptrup;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* recursively eat all tiles up, tiles that have eaten do not eat again.
|
||||
* start from top to bottom
|
||||
*/
|
||||
static int /* return 1 if change is made, 0 otherwise */
|
||||
eat(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int flag = 0; /* 1 if change is made, 0 otherwise */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 1; j < N; j++) {
|
||||
ARR tmp = { 0, 0 }; /* temporary arr of current tile */
|
||||
int *ptrtmp =
|
||||
NULL; /* temporary pointer to current tile */
|
||||
|
||||
tmp.x = i;
|
||||
tmp.y = j;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
asgn_if_src_true(&flag, eat1(map, ptrtmp));
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* one operation of moving up and eating tiles
|
||||
*/
|
||||
static int /* return 1 if change is made, 0 otherwise */
|
||||
upeat(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int haschanged = 0; /* 1 if change is made, 0 otherwise */
|
||||
|
||||
asgn_if_src_true(&haschanged, moveup(map));
|
||||
asgn_if_src_true(&haschanged, eat(map));
|
||||
asgn_if_src_true(&haschanged, moveup(map));
|
||||
return haschanged;
|
||||
}
|
||||
|
||||
/*
|
||||
* print top and bottom border of map
|
||||
*/
|
||||
static void
|
||||
puttb(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < N * 6 + 1; i++)
|
||||
putchar('-');
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* print middle border of map
|
||||
*/
|
||||
static void
|
||||
putm(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
putchar('|');
|
||||
for (i = 0; i < N * 6 - 1; i++)
|
||||
putchar('-');
|
||||
putchar('|');
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* print given row of map with numbers
|
||||
*/
|
||||
static void
|
||||
putnumline(int *map, /* pointer to firse element of map */
|
||||
int line /* row to print */
|
||||
)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
putchar('|');
|
||||
for (i = 0; i < N; i++) {
|
||||
ARR tmp = { 0, 0 }; /* temporary arr of current tile */
|
||||
int *ptrtmp = NULL; /* pointer to current tile */
|
||||
|
||||
tmp.x = i;
|
||||
tmp.y = line;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
if (*ptrtmp != 0)
|
||||
printf("%5d|", *ptrtmp);
|
||||
else
|
||||
printf(" |");
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* print map
|
||||
*/
|
||||
static void
|
||||
prt(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
puttb();
|
||||
putnumline(map, 0);
|
||||
for (i = 1; i < N; i++) {
|
||||
putm();
|
||||
putnumline(map, i);
|
||||
}
|
||||
puttb();
|
||||
}
|
||||
|
||||
/*
|
||||
* randomly generate 2 or 4 in empty tile in map
|
||||
*/
|
||||
static int /* return 1 if no empty tiles left, 0 otherwise */
|
||||
genrnd(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int rnd = 2 * (rand() % 2 + 1); /* random number, either 2 or 4 */
|
||||
int cnt = 0; /* count of empty tiles in map */
|
||||
ARR empty[N * N] = { { 0,
|
||||
0 } }; /* array of arr of empty tiles in map */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
ARR tmp = { 0, 0 }; /* temporary arr of current tile */
|
||||
int *ptrtmp = NULL; /* pointer to current tile */
|
||||
|
||||
tmp.x = j;
|
||||
tmp.y = i;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
if (*ptrtmp > 0)
|
||||
continue;
|
||||
empty[cnt] = tmp;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt == 0)
|
||||
return 1;
|
||||
*arr2ptr(map, empty[rand() % cnt]) = rnd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate highest number of current game
|
||||
*/
|
||||
static int /* return highest number of current game */
|
||||
calhinum(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int num = 0; /* current highest number */
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
ARR tmp = { 0, 0 }; /* temporary arr of current tile */
|
||||
int *ptrtmp =
|
||||
NULL; /* temporary pointer to current tile */
|
||||
|
||||
tmp.x = j;
|
||||
tmp.y = i;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
if (*ptrtmp <= num)
|
||||
continue;
|
||||
num = *ptrtmp;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if given tile is the same number as adjacent right or down tile
|
||||
*/
|
||||
static int /* return 1 if the same, 0 otherwise */
|
||||
same_as_follower(int *map, /* pointer to first element of map */
|
||||
int *ptr /* pointer to given tile */
|
||||
)
|
||||
{
|
||||
ARR tmp = ptr2arr(map, ptr); /* temporary arr of given tile */
|
||||
|
||||
if (tmp.x < N - 1) {
|
||||
ARR tmpright = {
|
||||
0, 0
|
||||
}; /* temporary arr of right of given tile */
|
||||
|
||||
tmpright.x = tmp.x + 1;
|
||||
tmpright.y = tmp.y;
|
||||
if (*ptr == *arr2ptr(map, tmpright))
|
||||
return 1;
|
||||
}
|
||||
if (tmp.y < N - 1) {
|
||||
ARR tmpdown = { 0,
|
||||
0 }; /* temporary arr of down of given tile */
|
||||
|
||||
tmpdown.x = tmp.x;
|
||||
tmpdown.y = tmp.y + 1;
|
||||
if (*ptr == *arr2ptr(map, tmpdown))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if game is over
|
||||
*/
|
||||
static int /* return 1 if game is over, 0 otherwise */
|
||||
isover(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < N; i++) {
|
||||
for (j = 0; j < N; j++) {
|
||||
ARR tmp = { 0, 0 }; /* temporary arr of current tile */
|
||||
int *ptrtmp =
|
||||
NULL; /* temporary pointer to current tile */
|
||||
|
||||
tmp.x = j;
|
||||
tmp.y = i;
|
||||
ptrtmp = arr2ptr(map, tmp);
|
||||
if (*ptrtmp == 0)
|
||||
return 0;
|
||||
if (same_as_follower(map, ptrtmp))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert input character to rotation time
|
||||
*/
|
||||
static int /* return rotation time, -1 if input is not defined */
|
||||
input_to_trans(char input /* input character */)
|
||||
{
|
||||
switch (input) {
|
||||
case 'w':
|
||||
return 0;
|
||||
case 's':
|
||||
return 2;
|
||||
case 'a':
|
||||
return 1;
|
||||
case 'd':
|
||||
return 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* one turn of game
|
||||
*/
|
||||
static void
|
||||
turn(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int haschanged = 0; /* 1 if change is made, 0 otherwise */
|
||||
char input = getch(); /* input character */
|
||||
int tmp = input_to_trans(input); /* rotation time */
|
||||
|
||||
if (tmp == -1)
|
||||
return;
|
||||
trans(map, tmp);
|
||||
haschanged = upeat(map);
|
||||
trans(map, (4 - tmp) % 4);
|
||||
if (!haschanged)
|
||||
return;
|
||||
system("cls");
|
||||
genrnd(map);
|
||||
prt(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* one game
|
||||
*/
|
||||
static void
|
||||
game(int *map /* pointer to first element of map */)
|
||||
{
|
||||
int end = 0; /* 1 if game has ended, 0 otherwise */
|
||||
|
||||
map_init(map);
|
||||
genrnd(map);
|
||||
system("cls");
|
||||
prt(map);
|
||||
while (!isover(map))
|
||||
turn(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* print gameover screen and update highest number and highest score
|
||||
*/
|
||||
static void
|
||||
gg(int *map /* pointer to first element of map */)
|
||||
{
|
||||
number = calhinum(map);
|
||||
if (number > hinum)
|
||||
hinum = number;
|
||||
if (score > hiscore)
|
||||
hiscore = score;
|
||||
printf("Score: %8d High Number: %5d Press space to continue...\n",
|
||||
score, number);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int map[N][N]; /* map of tiles */
|
||||
int *ptrmap = &map[0][0]; /* pointer to first element of map */
|
||||
|
||||
map_init(ptrmap);
|
||||
srand(time(NULL));
|
||||
do {
|
||||
system("cls");
|
||||
prtwelcome();
|
||||
if (getch() != ' ')
|
||||
break;
|
||||
game(ptrmap);
|
||||
gg(ptrmap);
|
||||
} while (getch() == ' ');
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
Loading…
Reference in New Issue