From 50d1f8b5f72528061e754f467b20e851dfe3dbe7 Mon Sep 17 00:00:00 2001 From: afrangry Date: Sun, 7 May 2023 21:09:41 +0800 Subject: [PATCH] First release of 2048 --- 2048.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ COPYING | 14 ++ README | 15 ++ 3 files changed, 604 insertions(+) create mode 100644 2048.c create mode 100644 COPYING create mode 100644 README diff --git a/2048.c b/2048.c new file mode 100644 index 0000000..dd6735a --- /dev/null +++ b/2048.c @@ -0,0 +1,575 @@ +/* + * 2048, by afrangry + * Copyright (c) 2023 Afrangry Hill + * 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 +#include +#include +#include + +#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; +} diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..ee7d6a5 --- /dev/null +++ b/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + 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. + diff --git a/README b/README new file mode 100644 index 0000000..31791bf --- /dev/null +++ b/README @@ -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 + +License + see LICENSE