Skip to content

Instantly share code, notes, and snippets.

@aoyama-val
Created January 16, 2024 22:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aoyama-val/c29894646016889796c7b4f36ad38e70 to your computer and use it in GitHub Desktop.
Save aoyama-val/c29894646016889796c7b4f36ad38e70 to your computer and use it in GitHub Desktop.
The COLUMNS game for UNIX terminal
/* COLUMNS for UNIX 1998.9.13 version */
/* by AKIKAWA,Hisashi (icb49250 @ nifty.ne.jp) */
/* ported to macOS/Linux by Shotaro Aoyama */
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include<termios.h>
#include<sys/time.h>
/*
#define RANDOM lrand48
#define SRANDOM srand48
*/
/* if lrand48() doesn't exist */
#define RANDOM random
#define SRANDOM srandom
#define LOCATE(x,y) printf("\33[%d;%dH",y+1,x+1)
#define CLS printf("\33[2J") /* 画面のクリア */
#define LINE printf("\33[4m") /* 下線をつける */
#define NOLINE printf("\33[0m") /* 下線なし */
#define WAIT(x) { timeout.tv_sec = 0; timeout.tv_usec = x * 1000;\
select(0,NULL,NULL,NULL,&timeout); } /* x msec 休む */
#define ROTATE 32
#define UP 65
#define DOWN 66
#define RIGHT 67
#define LEFT 68
#define INITSPEED 150
int main()
{
char c;
int loop,looop;
int x,y;
int xdir,ydir; /* 走査方向 */
int now[4],next[4]; /* 落ちてくる宝石 */
int state[8][16]; /* 画面の様子 */
int vanish[8][16]; /* 消えるところは1になる */
int speed = INITSPEED; /* 本当はスピードの逆数 */
int score = 0;
int level = 0;
int jewel = 0; /* 消した宝石の数 */
int line; /* 何ライン分消えたか */
struct timeval timeout;
struct termios termnew,termold;
char hankaku[8][3] = {" ","O","#","=","+","!","@","|"};
char zenkaku[8][3] = {" ","□","#","〓","★","▼","◎","|"};
char (*gem)[3] = hankaku;
tcgetattr(0,&termold);
termnew = termold;
termnew.c_lflag &= ~(ICANON | ECHO); /* エコーなし、非カノニカルモード */
termnew.c_cc[VMIN] = 0; /* read()から */
termnew.c_cc[VTIME] = 0; /* すぐ戻る */
tcsetattr(0,TCSANOW,&termnew);
CLS;
LOCATE(0,0);
NOLINE;
/* x = 0 1 2 3 4 5 6 7 y = 0 */
printf(" ------------ -- \n"); /* 1 */
printf(" | | | |\n"); /* 2 */
printf(" | | | |\n"); /* 3 */
printf(" | | | |\n"); /* 4 */
printf(" | | --\n"); /* 5 */
printf(" | |\n"); /* 6 */
printf(" | |\n"); /* 7 */
printf(" | | SCORE\n"); /* 8 */
printf(" | | 0\n"); /* 9 */
printf(" | | LEVEL\n"); /* 10 */
printf(" | | 0\n"); /* 11 */
printf(" | | JEWEL\n"); /* 12 */
printf(" | | 0\n"); /* 13 */
printf(" | |\n"); /* 14 */
printf(" ------------"); /* 15 */
LOCATE(4,4); /* 半角か全角か */
printf("\33[7mHankaku");
NOLINE;
LOCATE(4,8);
printf("Zenkaku");
LOCATE(21,12);
fflush(stdout);
do{
if( read(0,&c,1) ){
while( read(0,&c,1) ) /* 先行入力分とカーソルの前置きの排除 */
;
if( (c == DOWN && gem == hankaku) || (c == UP && gem == zenkaku) ){
NOLINE;
if( gem == hankaku ){
LOCATE(4,4);
printf("Hankaku");
LOCATE(4,8);
printf("\33[7mZenkaku");
gem = zenkaku;
}
else if( gem == zenkaku ){
LOCATE(4,8);
printf("Zenkaku");
LOCATE(4,4);
printf("\33[7mHankaku");
gem = hankaku;
}
LOCATE(21,12);
fflush(stdout);
}
}
}while( c != ROTATE );
LOCATE(5,2); /* レベルの選択 */
printf("\33[7mEasy\n");
NOLINE;
printf(" | LV0 -\n");
printf(" | + 0pts.\n\n");
printf(" | Medium\n");
printf(" | LV5 -\n");
printf(" | + 20000pts.\n\n");
printf(" | Hard\n");
printf(" | LV10 -\n");
printf(" | + 50000pts.");
LOCATE(21,12);
fflush(stdout);
while(1){
if( read(0,&c,1) ){
while( read(0,&c,1) ) /* 先行入力分とカーソルの前置きの排除 */
;
if( c == ROTATE )
break;
if( (c == UP && level >0) || (c == DOWN && level<10) ){
LOCATE(4,2+level/5*4);
NOLINE;
if( level == 0 )
printf(" Easy ");
if( level == 5 )
printf("Medium");
if( level == 10 )
printf(" Hard ");
if( c == UP )
level = level - 5;
if( c == DOWN )
level = level + 5;
LOCATE(4,2+level/5*4);
if( level == 0 )
printf("\33[7m Easy ");
if( level == 5 )
printf("\33[7mMedium");
if( level == 10 )
printf("\33[7m Hard ");
LOCATE(21,12);
fflush(stdout);
}
}
}
if( level == 5 ){
score = 20000;
speed = 15;
}
if( level == 10 ){
score = 50000;
speed = 7;
}
NOLINE;
LOCATE(0,2);
for(loop=0;loop<11;loop++)
printf(" | \n");
LOCATE(16,8);
printf("%5d",score);
LOCATE(18,10);
printf("%3d",level);
/* 底(値は10)と壁(値は7)を作る & 状態のクリア */
for(x=0;x<8;x++)
state[x][15] = 10;
for(y=0;y<15;y++){
state[0][y] = 7;
state[7][y] = 7;
for(x=1;x<7;x++){
state[x][y] = 0;
vanish[x][y] = 0;
}
}
SRANDOM((long)time(NULL));
for(loop=1;loop<4;loop++){
next[loop] = RANDOM()%6 + 1;
LOCATE(18,loop);
printf("%s",gem[next[loop]]);
}
now[0] = 0;
while(1){ /* ゲームオーバーまでループ */
for(loop=1;loop<4;loop++){
now[loop] = next[loop];
next[loop] = RANDOM()%6 + 1;
}
x = 4;
for(y=2;state[x][y]==0;y++){ /* 着地するまでループ(落下してゆく)*/
for(loop=3;(loop>=0 && y+loop>4);loop--){ /* 囲いつきで表示 */
LOCATE(x*2,y+loop-4);
if( loop == 0 || loop == 3 )
LINE;
else
NOLINE;
printf("%s",gem[now[loop]]);
NOLINE;
if( gem == hankaku ) /* 残った点の除去(半角) */
printf(" ");
if( gem == zenkaku ) /* 残った点の除去(全角) */
printf("%s",gem[state[x+1][y-3+loop]]);
}
if( y > 5 ){ /* 残った下線/点の除去 */
LOCATE(x*2,y-5);
printf(" %s",gem[state[x+1][y-4]]);
}
LOCATE(21,12);
fflush(stdout);
/* キー入力開始 */
for(looop=0;(state[x][y+1]==0 && looop<speed)||\
(state[x][y+1] && looop<speed+70) ;looop++){
WAIT(10);
if( read(0,&c,1) ){
while( read(0,&c,1) ) /* 先行入力分とカーソルの前置きの排除 */
;
if( c == 10 ){
NOLINE;
tcsetattr(0,TCSANOW,&termold);
CLS;
LOCATE(0,0);
exit(1);
}
if( c == DOWN )
looop = speed + 70;
if( (c == RIGHT && state[x+1][y] == 0) || \
(c == LEFT && state[x-1][y] == 0) || \
(c == ROTATE ) ){
NOLINE;
for(loop=3;(loop>=0 && y+loop>4);loop--){
LOCATE(x*2,y+loop-4);
printf(" ");
}
if( c == LEFT )
x--;
if( c == RIGHT )
x++;
if( c == ROTATE ){
int swap = now[3];
now[3] = now[2];
now[2] = now[1];
now[1] = swap;
if( y<4 ){
for(loop=1;loop<4;loop++){
LOCATE(18,loop);
printf("%s",gem[now[loop]]);
}
}
}
for(loop=3;(loop>=0 && y+loop>4);loop--){
LOCATE(x*2,y+loop-4);
if( loop == 0 || loop == 3 )
LINE;
else
NOLINE;
printf("%s",gem[now[loop]]);
NOLINE;
if( gem == hankaku ) /* 残った点の除去(半角) */
printf(" ");
if( gem == zenkaku ) /* 残った点の除去(全角) */
printf("%s",gem[state[x+1][y-3+loop]]);
}
LOCATE(21,12);
fflush(stdout);
}
}
}
/* ここまでキー入力 */
if(y == 3 || state[x][y+1] ){ /* 完全に顔を出すまで次を表示しない */
for(loop=1;loop<4;loop++){
LOCATE(18,loop);
printf("%s",gem[next[loop]]);
}
}
}
/* 着地 */
NOLINE;
for(loop=3;(loop>=0 && y+loop>5);loop--){
LOCATE(x*2,y-5+loop);
printf("%s",gem[now[loop]]);
}
LOCATE(21,12);
fflush(stdout);
for(loop=3;loop>0;loop--)
state[x][y+loop-4] = now[loop];
for(looop=1;;looop++){ /* 消えるかどうかの判断 */
line = 0;
for(ydir=1;ydir>=0;ydir--){
for(xdir=1;xdir>=-1;xdir--){
if( xdir ==0 && ydir == 0)
break;
for(x=1;x<7;x++){
for(y=2;y<15;y++){
if( state[x][y] && x <= 6-xdir*2 && x >= 1-xdir*2 && y <= 14-ydir*2 ){ /* ←無くても良い(ちょっと遅くなる) */
for(loop=1;loop<3;loop++)
if( state[x][y] != state[x + xdir*loop][y + ydir*loop] )
break;
if( loop == 3 ){
line++;
for(loop=0;loop<3;loop++)
vanish[x + xdir*loop][y + ydir*loop] = 1;
}
}
}
}
}
}
if( line == 0 ){
WAIT(900);
break;
}
LOCATE(17,5);
printf("%4d",line * looop * (level+1)*30);
score = score + line * looop * (level+1)*30;
if( score > 99999999 )
score = 99999999;
WAIT(130);
for(loop=0;loop<3;loop++){ /* 点滅 */
for(x=1;x<7;x++){
for(y=1;y<15;y++){
if( vanish[x][y] ){
LOCATE(x*2,y-1);
if( loop == 1 )
printf("%s",gem[state[x][y]]);
else
printf(" ");
if( loop == 2 ){
state[x][y] = 0;
if( jewel <9999 ){
jewel++;
if( jewel % 35 == 0 )
level++;
if( speed > 2 )
speed = INITSPEED / ( 1 + level*1.8 );
}
}
}
}
}
LOCATE(21,12);
fflush(stdout);
WAIT(100);
}
WAIT(70);
LOCATE(17,5);
printf(" ");
for(loop=0;loop<1;loop++){ /* 消えた分、落下 */
WAIT(30);
for(x=1;x<7;x++){
for(y=14;y>=0;y--){
if( vanish[x][y] ){
loop = -1; /* さらに落ちる */
if( y == 0 ){
state[x][0] = 0;
vanish[x][0] = 0;
}
else{
state[x][y] = state[x][y-1];
vanish[x][y] = vanish[x][y-1];
vanish[x][y-1] = 1; /* 消えたところに空白ができる */
if( y > 1 ){
LOCATE(x*2,y-1);
printf("%s",gem[state[x][y]]);
}
}
}
}
}
LOCATE(21,12);
fflush(stdout);
}
LOCATE(16,8);
printf("%5d",score);
LOCATE(18,10);
printf("%3d",level);
LOCATE(17,12);
printf("%4d",jewel);
fflush(stdout);
}
for(x=1;x<7;x++){ /* ゲームオーバー */
if( state[x][1] || state[4][2] ){
tcsetattr(0,TCSANOW,&termold);
exit(1);
}
}
while( read(0,&c,1) ) /* 先行入力分の排除 */
;
}
}
@aoyama-val
Copy link
Author

Original

http://000.la.coocan.jp/columns.html

WAIT(selectでsleepを代用)

usleep(3) 関数を持たないシステムでは、 有限のタイムアウトを指定し、ファイルディスクリプタを全くセットせずに select() を呼び出すことで、これを代用できる。 以下のようにする。

    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 200000;  /* 0.2 seconds */
    select(0, NULL, NULL, NULL, &tv);

但し、これが動くと保証されているのは UNIX システムに限られる。
https://ja.manpages.org/select/2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment