aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-08-24 10:27:59 +0900
committernsfisis <nsfisis@gmail.com>2025-08-24 13:34:43 +0900
commit60613d00bbd7ad3541670d26dc27073810c53c29 (patch)
tree70c251ecffe7b98bae9117c7191c50f49267a358
parent307dd4110d0dd2e1246288394f8a2b46a2e55add (diff)
download2048.c-60613d00bbd7ad3541670d26dc27073810c53c29.tar.gz
2048.c-60613d00bbd7ad3541670d26dc27073810c53c29.tar.zst
2048.c-60613d00bbd7ad3541670d26dc27073810c53c29.zip
rewrite without using ncursesHEADmain
-rw-r--r--2048.c51
-rw-r--r--2048_clean.c111
-rw-r--r--Makefile11
-rw-r--r--README.md18
-rw-r--r--orig/2048.c33
-rw-r--r--orig/2048_clean.c202
6 files changed, 344 insertions, 82 deletions
diff --git a/2048.c b/2048.c
index bdd9d1f..1e7e0cb 100644
--- a/2048.c
+++ b/2048.c
@@ -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);
}
diff --git a/Makefile b/Makefile
index 47367df..4b69626 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 1862a2f..a14a743 100644
--- a/README.md
+++ b/README.md
@@ -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;
+}