Created
January 16, 2024 22:04
-
-
Save aoyama-val/c29894646016889796c7b4f36ad38e70 to your computer and use it in GitHub Desktop.
The COLUMNS game for UNIX terminal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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) ) /* 先行入力分の排除 */ | |
; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Original
http://000.la.coocan.jp/columns.html
WAIT(selectでsleepを代用)