diff options
| -rw-r--r-- | 2048.c | 51 | ||||
| -rw-r--r-- | 2048_clean.c | 111 | ||||
| -rw-r--r-- | Makefile | 11 | ||||
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | orig/2048.c | 33 | ||||
| -rw-r--r-- | orig/2048_clean.c | 202 |
6 files changed, 344 insertions, 82 deletions
@@ -1,33 +1,32 @@ -#include <ncurses.h> #include <stdio.h> #include <stdlib.h> +#include <termios.h> #include <time.h> #include <unistd.h> -#define s(_)waddstr(W,_) -#define c(_)waddch(W,_) -#define f(f,_)wprintw(W,f,_) -#define n c('\n') +#define f(f,_)printf(f,_) +#define s(_)f("%s",_) +#define n s("\r\n") #define r(_,n)for(int _=0;_<n;++_) -#define t(_)G+y*((x+S)%(S+1)+_*x)+(S+1-y)*i +#define t(_)(G+y*((x+S)%(S+1)+_*x)+(S+1-y)*i) -WINDOW*W;char*M="\0" ;int S,R,C,N,E,*L,*T ,F[128], *G=F;void _(){c('+');r(i,8*S-1 -){c('-');}c('+');n;} void l(){c('|');r(i, S){r(j,8 ){c(32); }c('\b');c('|');}n;} -void g(){_();r(i,S){ l();s("| ");r(j,S){ if(!*G){ s(" " " | ");}else if(*G>= -1024){f(" %2dk | ", *G/1024);}else{f( "%4d | " ,*G);}++ G;}n;l();if(i<S-1){c - ('|' ); r(j,S){r (j,8){c( '-');}c( '\b');if (j<S-1){ c('+');} - }c('|'); n;}}_(); G-=S*S;} int m(int x,int y, int d){C =0;r(i,S - ){E=0;L= 0;r(j,S) {T=t(j); if(*T){if (L&& *T== *L){if(d ){*L*=2; - *T=0;}L= 0;C=1;} else{L= t(E);if( d){*L=*T ;}++E;}} }for(;E< -S;++E){T=t(E);C|=*T; if(d){*T =0;}}} return!C;}void p(){E=0;r(i,S*S){if(!G[i]){ -++E;}}if(E==0){return ;}N=rand ()%E;r(i ,S*S){if(!G[i]&&--E ==N){G[i]=rand()%8?2 -:4;}}}int main(int argc,char **argv){ srand(time(0));initscr();cbreak();noecho(); -curs_set(0);getmaxyx (stdscr, R,C);S= argc>1?(*argv[1]-'0' ):4;if(S<=1||S>8){M= -"\1Invalid size giv" "en.\n"; goto q;} W=newwin(0,0,(R-4*S) /2,(C-8*S)/2);if(!W) -{M="\1T" "ermina" "l too " "small." "\n";goto q;}p();p -();while (1){wclear (W);g(); s(" h," "j,k,l:" " move " -" " " q: q" "uit"); wrefresh (W);switch (wgetch( -W)){case 'q':goto q;case'h' :m(1,1,1 );goto e; case'j': +int S,N,E,C,*L,*T,F[ 128],*G= F,R ;struct termios O ;void x( ){if(!R++)tcsetattr( +0,2,&O),s("\e[?25h\e" "[?1049l");}void _() {s("+"); r(i,8*S- 1)s("-");s("+");n;} +void l(){s("|");r(i, S){r(j,7)s(" ");s("|" );}n;}int m(int x, int y,int d){C=0;r(i +,S){E=0;L=0;r(j,S)if (*(T=t(j)))if(L&&*T ==*L){if (d){*L*= 2;T=0;}L=0;C=1;}else + {L=t(E); if(d)*L= *T;++E;} for(;E<S ;++E){T= t(E);C|= *T;if(d) + *T=0; }} return!C ;}void p (){ E =0 ;r( i,S* S)if (!G [i])++E; + if(E==0) return;N =rand()% E;r(i,S* S)if(!G[ i]&&--E== N)G[i]= + rand()%8 ?2:4;}int main(int argc,char **argv){ srand(time(0));atexit +(x); s("\e[?1049h"); tcgetattr (0, &O); struct termios o=O;o .c_iflag&=-1331;o. +c_oflag&=-2;o.c_cflag |=48;o. c_lflag&= -32780;o.c_cc[6]=0;o .c_cc[5]=1;tcsetattr +(0,2,&o);s("\e[?25l" );S=argc> 1?(*argv [1]-48):4;if(S<2||S> 8){fputs("invalid b" +"oard size\n",stderr );return 1;}p();p ();for(;;){s("\e[2" "J\e[H");_();r(i,S){ +l();s("| " );r(j,S) {if(!*G) s(" " " | ");else if(*G>= +1024)f(" " "%2dk " "| ",*G/ 1024);else f("%4d | ",*G);++G +;}n;l(); if(i<S-1 ){s("|") ;r(j,S){ r(j,7)s( "-");if( +j<S-1)s( "+");}s( "|");n;} }_();G-= S*S;s(" " "h,j,k," +"l: move");r(i,9)s(" ");s("q: quit");fflush (stdout) ;char k;while(read(0 +,&k,1)!=1);switch(k) {case'q':goto q;case 'h':m(1, 1,1);goto e;case'j': m(-1,S,1);goto e;case 'k':m(1,S,1);goto e; case'l': m(-1,1,1);goto e;e:if -(C){p();}if(m(1,1,0) &m(-1,1,0)&m(1,S,0)& m(-1,S,0 )){M="\0Game over.\n" -;goto q;}usleep(2048 *8);break;}}q:if(W){ delwin(W );}endwin();fputs(M+ -1,stderr);return*M;} /*2^11*/ +(C)p();if(m(1,1,0)&m (-1,1,0)&m(1,S,0)&m( -1,S,0)) goto q;usleep(2048*8 +);break;}}q:x();s("\e[2J\e[H");}/* 2048 in 2 kb by nsfisis */ diff --git a/2048_clean.c b/2048_clean.c index ac58325..79a474b 100644 --- a/2048_clean.c +++ b/2048_clean.c @@ -1,24 +1,69 @@ -#include <ncurses.h> #include <stdio.h> #include <stdlib.h> +#include <termios.h> #include <time.h> #include <unistd.h> -#define waddstr_(text) waddstr(main_window, text) -#define waddch_(ch) waddch(main_window, ch) -#define wprintw_(format, data) wprintw(main_window, format, data) -#define newline waddch_('\n') +#define waddstr_(text) printf("%s", text) +#define waddch_(ch) putchar(ch) +#define wprintw_(format, data) printf(format, data) +#define newline printf("\r\n") #define repeat(i, n) for (int i = 0; i < n; ++i) #define get_tile_at(k) (grid + y * ((x + size) % (size + 1) + k * x) + (size + 1 - y) * i) -WINDOW *main_window; -char *message = "\0"; -int size, num_rows, num_columns, new_tile_pos, num_empty_slots, changed, last_empty_tile_index, *last_non_empty_tile, - *this_tile, grid_buf[128], *grid = grid_buf; +#define CLEAR_SCREEN "\033[2J\033[H" +#define HIDE_CURSOR "\033[?25l" +#define SHOW_CURSOR "\033[?25h" -void draw_frame_1() { +int size, new_tile_pos, num_empty_slots, changed, last_empty_tile_index, *last_non_empty_tile, *this_tile, + grid_buf[128], *grid = grid_buf, did_restore_from_alt_buffer, did_disable_raw_mode; + +void restore_from_alt_buffer(void) { + if (did_restore_from_alt_buffer++) + return; + printf("\033[?1049l"); +} + +void switch_to_alt_buffer(void) { + atexit(restore_from_alt_buffer); + printf("\033[?1049h"); +} + +struct termios original_termios; + +void disable_raw_mode(void) { + if (did_disable_raw_mode++) + return; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios); + printf(SHOW_CURSOR); +} + +void enable_raw_mode(void) { + tcgetattr(STDIN_FILENO, &original_termios); + atexit(disable_raw_mode); + + struct termios raw = original_termios; + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); + printf(HIDE_CURSOR); +} + +char get_key(void) { + char c; + while (read(STDIN_FILENO, &c, 1) != 1) + ; + return c; +} + +void draw_frame_1(void) { waddch_('+'); repeat(i, 8 * size - 1) { waddch_('-'); @@ -27,19 +72,18 @@ void draw_frame_1() { newline; } -void draw_frame_2() { +void draw_frame_2(void) { waddch_('|'); repeat(i, size) { - repeat(j, 8) { + repeat(j, 7) { waddch_(32); } - waddch_('\b'); waddch_('|'); } newline; } -void show_grid() { +void show_grid(void) { draw_frame_1(); repeat(i, size) { draw_frame_2(); @@ -62,10 +106,9 @@ void show_grid() { if (i < size - 1) { waddch_('|'); repeat(j, size) { - repeat(j, 8) { + repeat(j, 7) { waddch_('-'); } - waddch_('\b'); if (j < size - 1) { waddch_('+'); } @@ -113,7 +156,7 @@ int move_grid(int x, int y, int do_change) { return !changed; } -void put_new_tile() { +void put_new_tile(void) { num_empty_slots = 0; repeat(i, size * size) { if (!grid[i]) { @@ -134,34 +177,25 @@ void put_new_tile() { int main(int argc, char **argv) { srand(time(0)); - initscr(); - cbreak(); - noecho(); - curs_set(0); - getmaxyx(stdscr, num_rows, num_columns); + switch_to_alt_buffer(); + enable_raw_mode(); size = argc > 1 ? (*argv[1] - '0') : 4; if (size <= 1 || size > 8) { - message = "\1Invalid size given.\n"; - goto quit; - } - - main_window = newwin(0, 0, (num_rows - 4 * size) / 2, (num_columns - 8 * size) / 2); - if (!main_window) { - message = "\1Terminal too small.\n"; - goto quit; + fputs("invalid board size\n", stderr); + return 1; } put_new_tile(); put_new_tile(); while (1) { - wclear(main_window); + printf(CLEAR_SCREEN); show_grid(); waddstr_(" h,j,k,l: move q: quit"); - wrefresh(main_window); + fflush(stdout); - switch (wgetch(main_window)) { + switch (get_key()) { case 'q': goto quit; @@ -183,7 +217,6 @@ int main(int argc, char **argv) { put_new_tile(); } if (move_grid(1, 1, 0) & move_grid(-1, 1, 0) & move_grid(1, size, 0) & move_grid(-1, size, 0)) { - message = "\0Game over.\n"; goto quit; } usleep(2048 * 8); @@ -192,11 +225,7 @@ int main(int argc, char **argv) { } quit: - if (main_window) { - delwin(main_window); - } - endwin(); - - fputs(message + 1, stderr); - return *message; + disable_raw_mode(); + restore_from_alt_buffer(); + printf(CLEAR_SCREEN); } @@ -4,15 +4,10 @@ DIST := bin PROGRAM := $(DIST)/2048 -all: run - -run: build - ./$(PROGRAM) - -build: $(DIST) $(PROGRAM) +all: $(DIST) $(PROGRAM) $(PROGRAM): 2048$(SUFFIX).c - gcc $^ -lncurses -o $@ + gcc $^ -o $@ $(DIST): @mkdir $@ @@ -20,4 +15,4 @@ $(DIST): clean: @rm -rf $(DIST) -.PHONY: all run build clean +.PHONY: all clean @@ -1,22 +1,24 @@ # 2048 -CLI 2048 written in 2 KiB. +CLI 2048 written in 2 kb. ``` -$ wc -c *.c - 2048 2048.c - 4930 2048_clean.c - 6978 total +$ wc -c **/*.c + 2048 2048.c + 5574 2048_clean.c + 2048 orig/2048.c + 4930 orig/2048_clean.c +14600 total ``` ## Compile ``` -$ make build +$ make ``` -By default, `2048.c` is compiled. If you want to compile another source file, set `SUFFIX` like this: `SUFFIX=_clean make build` (it compiles `2048_clean.c`) +By default, `2048.c` is compiled. If you want to compile another source file, set `SUFFIX` like this: `SUFFIX=_clean make` (it compiles `2048_clean.c`) ## Play @@ -40,3 +42,5 @@ $ ./bin/2048 8x8 # large 2048 * Minified version. Its file size is 2 KiB. * `2048_clean.c` * Human-friendly version. +* `orig/2024.c` and `orig/2024_clean.c` + * Original version. They depends on `ncurses` library. diff --git a/orig/2048.c b/orig/2048.c new file mode 100644 index 0000000..bdd9d1f --- /dev/null +++ b/orig/2048.c @@ -0,0 +1,33 @@ +#include <ncurses.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#define s(_)waddstr(W,_) +#define c(_)waddch(W,_) +#define f(f,_)wprintw(W,f,_) +#define n c('\n') +#define r(_,n)for(int _=0;_<n;++_) +#define t(_)G+y*((x+S)%(S+1)+_*x)+(S+1-y)*i + +WINDOW*W;char*M="\0" ;int S,R,C,N,E,*L,*T ,F[128], *G=F;void _(){c('+');r(i,8*S-1 +){c('-');}c('+');n;} void l(){c('|');r(i, S){r(j,8 ){c(32); }c('\b');c('|');}n;} +void g(){_();r(i,S){ l();s("| ");r(j,S){ if(!*G){ s(" " " | ");}else if(*G>= +1024){f(" %2dk | ", *G/1024);}else{f( "%4d | " ,*G);}++ G;}n;l();if(i<S-1){c + ('|' ); r(j,S){r (j,8){c( '-');}c( '\b');if (j<S-1){ c('+');} + }c('|'); n;}}_(); G-=S*S;} int m(int x,int y, int d){C =0;r(i,S + ){E=0;L= 0;r(j,S) {T=t(j); if(*T){if (L&& *T== *L){if(d ){*L*=2; + *T=0;}L= 0;C=1;} else{L= t(E);if( d){*L=*T ;}++E;}} }for(;E< +S;++E){T=t(E);C|=*T; if(d){*T =0;}}} return!C;}void p(){E=0;r(i,S*S){if(!G[i]){ +++E;}}if(E==0){return ;}N=rand ()%E;r(i ,S*S){if(!G[i]&&--E ==N){G[i]=rand()%8?2 +:4;}}}int main(int argc,char **argv){ srand(time(0));initscr();cbreak();noecho(); +curs_set(0);getmaxyx (stdscr, R,C);S= argc>1?(*argv[1]-'0' ):4;if(S<=1||S>8){M= +"\1Invalid size giv" "en.\n"; goto q;} W=newwin(0,0,(R-4*S) /2,(C-8*S)/2);if(!W) +{M="\1T" "ermina" "l too " "small." "\n";goto q;}p();p +();while (1){wclear (W);g(); s(" h," "j,k,l:" " move " +" " " q: q" "uit"); wrefresh (W);switch (wgetch( +W)){case 'q':goto q;case'h' :m(1,1,1 );goto e; case'j': +m(-1,S,1);goto e;case 'k':m(1,S,1);goto e; case'l': m(-1,1,1);goto e;e:if +(C){p();}if(m(1,1,0) &m(-1,1,0)&m(1,S,0)& m(-1,S,0 )){M="\0Game over.\n" +;goto q;}usleep(2048 *8);break;}}q:if(W){ delwin(W );}endwin();fputs(M+ +1,stderr);return*M;} /*2^11*/ diff --git a/orig/2048_clean.c b/orig/2048_clean.c new file mode 100644 index 0000000..ac58325 --- /dev/null +++ b/orig/2048_clean.c @@ -0,0 +1,202 @@ +#include <ncurses.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#define waddstr_(text) waddstr(main_window, text) +#define waddch_(ch) waddch(main_window, ch) +#define wprintw_(format, data) wprintw(main_window, format, data) +#define newline waddch_('\n') + +#define repeat(i, n) for (int i = 0; i < n; ++i) + +#define get_tile_at(k) (grid + y * ((x + size) % (size + 1) + k * x) + (size + 1 - y) * i) + +WINDOW *main_window; +char *message = "\0"; +int size, num_rows, num_columns, new_tile_pos, num_empty_slots, changed, last_empty_tile_index, *last_non_empty_tile, + *this_tile, grid_buf[128], *grid = grid_buf; + +void draw_frame_1() { + waddch_('+'); + repeat(i, 8 * size - 1) { + waddch_('-'); + } + waddch_('+'); + newline; +} + +void draw_frame_2() { + waddch_('|'); + repeat(i, size) { + repeat(j, 8) { + waddch_(32); + } + waddch_('\b'); + waddch_('|'); + } + newline; +} + +void show_grid() { + draw_frame_1(); + repeat(i, size) { + draw_frame_2(); + + waddstr_("| "); + repeat(j, size) { + if (!*grid) { + waddstr_(" | "); + } else if (*grid >= 1024) { + wprintw_(" %2dk | ", *grid / 1024); + } else { + wprintw_("%4d | ", *grid); + } + ++grid; + } + newline; + + draw_frame_2(); + + if (i < size - 1) { + waddch_('|'); + repeat(j, size) { + repeat(j, 8) { + waddch_('-'); + } + waddch_('\b'); + if (j < size - 1) { + waddch_('+'); + } + } + waddch_('|'); + newline; + } + } + draw_frame_1(); + grid -= size * size; +} + +int move_grid(int x, int y, int do_change) { + changed = 0; + repeat(i, size) { + last_empty_tile_index = 0; + last_non_empty_tile = 0; + repeat(j, size) { + this_tile = get_tile_at(j); + if (*this_tile) { + if (last_non_empty_tile && *this_tile == *last_non_empty_tile) { + if (do_change) { + *last_non_empty_tile *= 2; + *this_tile = 0; + } + last_non_empty_tile = 0; + changed = 1; + } else { + last_non_empty_tile = get_tile_at(last_empty_tile_index); + if (do_change) { + *last_non_empty_tile = *this_tile; + } + ++last_empty_tile_index; + } + } + } + for (; last_empty_tile_index < size; ++last_empty_tile_index) { + this_tile = get_tile_at(last_empty_tile_index); + changed |= *this_tile; + if (do_change) { + *this_tile = 0; + } + } + } + return !changed; +} + +void put_new_tile() { + num_empty_slots = 0; + repeat(i, size * size) { + if (!grid[i]) { + ++num_empty_slots; + } + } + if (num_empty_slots == 0) { + return; + } + new_tile_pos = rand() % num_empty_slots; + repeat(i, size * size) { + if (!grid[i] && --num_empty_slots == new_tile_pos) { + grid[i] = rand() % 8 ? 2 : 4; + } + } +} + +int main(int argc, char **argv) { + srand(time(0)); + + initscr(); + cbreak(); + noecho(); + curs_set(0); + getmaxyx(stdscr, num_rows, num_columns); + + size = argc > 1 ? (*argv[1] - '0') : 4; + if (size <= 1 || size > 8) { + message = "\1Invalid size given.\n"; + goto quit; + } + + main_window = newwin(0, 0, (num_rows - 4 * size) / 2, (num_columns - 8 * size) / 2); + if (!main_window) { + message = "\1Terminal too small.\n"; + goto quit; + } + + put_new_tile(); + put_new_tile(); + + while (1) { + wclear(main_window); + show_grid(); + waddstr_(" h,j,k,l: move q: quit"); + wrefresh(main_window); + + switch (wgetch(main_window)) { + case 'q': + goto quit; + + case 'h': + move_grid(1, 1, 1); + goto move_end; + case 'j': + move_grid(-1, size, 1); + goto move_end; + case 'k': + move_grid(1, size, 1); + goto move_end; + case 'l': + move_grid(-1, 1, 1); + goto move_end; + + move_end: + if (changed) { + put_new_tile(); + } + if (move_grid(1, 1, 0) & move_grid(-1, 1, 0) & move_grid(1, size, 0) & move_grid(-1, size, 0)) { + message = "\0Game over.\n"; + goto quit; + } + usleep(2048 * 8); + break; + } + } + +quit: + if (main_window) { + delwin(main_window); + } + endwin(); + + fputs(message + 1, stderr); + return *message; +} |
