First release of 2048

This commit is contained in:
afrangry 2023-05-07 21:09:41 +08:00
commit 50d1f8b5f7
3 changed files with 604 additions and 0 deletions

575
2048.c Normal file
View File

@ -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;
}

14
COPYING Normal file
View File

@ -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.

15
README Normal file
View File

@ -0,0 +1,15 @@
2048 by afrangry
options
change N to what you want
TODO
add makefile
make sys("clear") system-independant
make conio.h system-independant
Contact
afrangry <donotcalllist@privacyrequired.com>
License
see LICENSE