Skip to content

Instantly share code, notes, and snippets.

@kamiyaowl
Last active August 29, 2015 14:03
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 kamiyaowl/6185fe79409217f8bf88 to your computer and use it in GitHub Desktop.
Save kamiyaowl/6185fe79409217f8bf88 to your computer and use it in GitHub Desktop.
tetris for Arduino [test]
#include<stdio.h>
#include <stdint.h>
#define LINEAR_RAND_A 61
#define LINEAR_RAND_C 5
uint16_t rnd_current = 45;//TODO:Enviroment value here
uint16_t rnd_init(uint16_t seed){
rnd_current = seed;
}
uint16_t rnd_next(){
rnd_current = (rnd_current * LINEAR_RAND_A + LINEAR_RAND_C) & 0xffff;
return rnd_current;
}
#include <stdio.h>
#include <stdint.h>
#include "linear_rand.h"
//definitions
#define WIDTH 20
#define HEIGHT 20
#define CENTER ((WIDTH / 2) - 2)
#define true 1
#define false 0
#define TETRIMINO_SIZE 16
#define WALL 'X'
#define EMPTY ' '
#define FILL '#'
#define TETRIMINO_STICK 0
#define TETRIMINO_SQUARE 1
#define TETRIMINO_S 2
#define TETRIMINO_Z 3
#define TETRIMINO_J 4
#define TETRIMINO_L 5
#define TETRIMINO_T 6
#define TETRIMINO_PATTERNS 7
#define MOVE_NONE 0
#define MOVE_UP 1
#define SPIN_NONE 0
#define MOVE_DOWN 2
#define MOVE_LEFT 3
#define MOVE_RIGHT 4
#define SPIN_LEFT 5
#define SPIN_RIGHT 6
typedef int boolean;
char field[HEIGHT][WIDTH];
uint16_t current_x,current_y;
boolean current_enable;
uint8_t current_tetrimino[TETRIMINO_SIZE];
uint8_t spin_tetrimino[TETRIMINO_SIZE];
uint8_t next_tetrimino[TETRIMINO_SIZE];
uint8_t empty_lines[HEIGHT];
uint16_t hitcount;
//progmem
uint8_t tetriminos[][TETRIMINO_SIZE] = {
{ 1, 1, 1, 1,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 1, 1, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 0, 1, 1, 0,
1, 1, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 1, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 1, 0, 0, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 0, 0, 1, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
{ 0, 1, 0, 0,
1, 1, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0, },
};
/* private functions */
uint8_t convert(uint8_t x,uint8_t y) {
return x + (y << 2);
}
//テトリミノが指定位置に配置可能か返します
boolean valid_check(uint8_t* tetrimino,uint16_t x,uint16_t y) {
if(x < 0 || WIDTH <= x || y < 0 || HEIGHT <= y) return false;
for(uint8_t j = 0 ; j < 4 ; ++j) {
for(uint8_t i = 0 ; i < 4 ; ++i) {
uint8_t p = convert(i,j);
if(!tetrimino[p]) continue;
else if(field[y + j][x + i] != EMPTY) return false;
}
}
return true;
}
//落下可能かを返します
boolean fall_check() {
if(!valid_check(current_tetrimino,current_x,current_y + 1)) return false;
//最下段チェック
for(uint8_t i = 0 ; i < 4 ; ++i) {
uint8_t p = convert(i,current_y);
if(current_tetrimino[p]) {
if(field[current_y + 1][current_x + i] != EMPTY) return false;
}
}
return true;
}
//テトリミノをコピーします
void copy_tetrimino(uint8_t* src,uint8_t* dst) {
for(uint8_t i = 0 ; i < TETRIMINO_SIZE ; ++i) {
dst[i] = src[i];
}
}
//ユーザー制御のテトリミノを初期位置に表示します
void reset_current_tetrimino() {
//current init
current_x = CENTER;
current_y = 0;
current_enable = true;
}
//予告テトリミノとユーザーテトリミノを作成します
//初期状態では予告テトリミノは不定なので予め代入する必要があります
boolean create_current_tetrimino() {
current_enable = false;
copy_tetrimino(next_tetrimino,current_tetrimino);
uint8_t next = rnd_next() % TETRIMINO_PATTERNS;
copy_tetrimino(tetriminos[next], next_tetrimino);
reset_current_tetrimino();
//game over?
if(valid_check(current_tetrimino,current_x,current_y)) return true;
else return false;
}
//ユーザーテトリミノをフィールドに反映します
//ユーザーテトリミノ自体は非表示になります
void fieldmap_current_tetrimino(){
current_enable = false;
for(uint8_t j = 0 ; j < 4 ; ++j) {
for(uint8_t i = 0 ; i < 4 ; ++i) {
uint8_t p = convert(i,j);
if(current_tetrimino[p]) {
//set field
field[current_y + j][current_x + i] = FILL;
}
}
}
}
//最速落下位置を返します
uint16_t move_current_fastfall(){
uint16_t i,j;
for(j = current_y ; j < HEIGHT ; ++j) {
for(i = 0 ; i < 4 ; ++i) {
if(field[j][i + current_x] == FILL) return j - 3;
}
}
return j - 3;
}
//テトリミノの移動を行います
boolean move_current_tetrimino(uint8_t param) {
uint16_t nx = current_x;
uint16_t ny = current_y;
switch(param) {
case MOVE_NONE:
return true;
case MOVE_UP:
//一番下まで落とす
ny = move_current_fastfall();
break;
case MOVE_DOWN:
++ny;
break;
case MOVE_LEFT:
--nx;
break;
case MOVE_RIGHT:
++nx;
break;
default:
return false;
}
//valid
if(ny < current_y) return false;
if(!valid_check(current_tetrimino,nx,ny)) return false;
//update current
current_x = nx;
current_y = ny;
return true;
}
//ユーザーテトリミノを回転します、
//回転結果はspin_teriminoに格納されます
void spin_right() {
for(uint8_t j = 0 ; j < 4 ; ++j) {
for(uint8_t i = 0 ; i < 4 ; ++i) {
uint8_t dst_p = convert(i,j);
uint8_t src_p = convert(j,3 - i);
spin_tetrimino[dst_p] = current_tetrimino[src_p];
}
}
}
//ユーザーテトリミノを回転します、
//回転結果はspin_teriminoに格納されます
void spin_left() {
for(uint8_t j = 0 ; j < 4 ; ++j) {
for(uint8_t i = 0 ; i < 4 ; ++i) {
uint8_t dst_p = convert(i,j);
uint8_t src_p = convert(3 - j,i);
spin_tetrimino[dst_p] = current_tetrimino[src_p];
}
}
}
//テトリミノを回転させます
boolean spin_current_tetrimino(uint8_t param) {
switch(param) {
case SPIN_NONE:
return true;
case SPIN_RIGHT:
spin_right();
break;
case SPIN_LEFT:
spin_left();
break;
default:
return false;
}
if(!valid_check(spin_tetrimino,current_x,current_y)) return false;
copy_tetrimino(spin_tetrimino,current_tetrimino);
return true;
}
//テユーザートリミノを落下させます
boolean fall_current_tetrimino() {
if(fall_check()) {
//can shift
++current_y;
} else {
//fix and next
fieldmap_current_tetrimino();
//ゲームオーバー判定
if(!create_current_tetrimino()) return false;
}
return true;
}
//揃った列を削除して、消した行数を返します
//消した列にはempty_linesフラグが立ちます
uint8_t field_fillline_break(){
uint8_t hit = 0;//得点計算用
for(uint16_t j = 0 ; j < HEIGHT ; ++j) {
boolean fill_flag = true;
for(uint16_t i = 0 ; i < WIDTH ; ++i) {
//check
if(field[j][i] == EMPTY) {
fill_flag = false;
break;
}
}
if(fill_flag) {
++hit;
//emptyline_fallのためにメモしておく
empty_lines[j] = true;
//remove
for(uint16_t i = 0 ; i < HEIGHT ; ++i) {
if(field[j][i] == FILL)
field[j][i] = EMPTY;
}
} else {
empty_lines[j] = false;
}
}
return hit;
}
//empty_flagの立っている列を落下させ切り詰めます
void filed_emptyline_fall() {
uint16_t src_y;
uint16_t dst_y = HEIGHT - 1;
for(int16_t j = dst_y ; j >= 0 ; --j){
if(!empty_lines[j]) {
//ずれている時だけ移動する
if(dst_y != j) {
for(uint16_t i = 0 ; i < WIDTH ; ++i) {
field[dst_y][i] = field[j][i];
}
}
--dst_y;
} else {
//空行はスキップ
empty_lines[j] = false;
continue;
}
}
}
//スコア計算をします
uint16_t calc_score(uint8_t count) {
switch(count) {
case 0:
return 0;
case 1:
return 10;
case 2:
return 50;
case 3:
return 200;
case 4:
return 500;
default:
return 0;
}
}
/* public functions */
//予告テトリミノを表示します
void tetris_next_print(){
printf("+--next--+\n| |\n");
for(uint16_t j = 0 ; j < 4 ; ++j) {
printf("| ");
for(uint16_t i = 0 ; i < 4 ; ++i){
uint8_t p = convert(i,j);
if(next_tetrimino[p]) putchar(WALL);
else putchar(EMPTY);
}
printf(" |\n");
}
printf("+--------+\n");
}
//現在のゲーム状態を表示します
void tetris_print(){
for(uint16_t j = 0 ; j < HEIGHT ; ++j) {
for(uint16_t i = 0 ; i < WIDTH ; ++i) {
uint8_t dx = i - current_x;
uint8_t dy = j - current_y;
uint8_t p = convert(dx,dy);
//area check
if(current_enable &&
(current_x <= i && i < current_x + 4) &&
(current_y <= j && j < current_y + 4) &&
(current_tetrimino[p])) {
putchar(FILL);
} else {
putchar(field[j][i]);
}
}
printf("\n");
}
}
//ゲームを初期化します
void tetris_init(){
//field init
for(uint16_t j = 0 ; j < HEIGHT ; ++j) {
for(uint16_t i = 0 ; i < WIDTH ; ++i) {
if(i <= 1 || i >= WIDTH - 2 )//|| j == HEIGHT - 1)
field[j][i] = WALL;
else
field[j][i] = EMPTY;
}
empty_lines[j] = false;
}
//tetrimino init
uint8_t next = rnd_next() % TETRIMINO_PATTERNS;
copy_tetrimino(tetriminos[next], next_tetrimino);
create_current_tetrimino();
//eval
hitcount = 0;
}
//毎回呼び出すことでゲームが進行します
//paramを与えることで操作することができます
//ゲームオーバー時にfalseが返り、hitcountに特典が格納されます
int tetris_update(uint8_t param){
if(!move_current_tetrimino(param)) {
//printf("[DEBUG] cannnot move tetrimino\n");
}
if(!spin_current_tetrimino(param)) {
//printf("[DEBUG] connnot spin tetrimino\n");
}
if(!fall_current_tetrimino()) {
//printf("[DEBUG] game over\n");
return false;
}
uint8_t count = field_fillline_break();
hitcount += calc_score(count);
filed_emptyline_fall();
return true;
}
void tetris_debugprint() {
printf("(%d, %d) : current\t%d : enable\n",current_x,current_y,current_enable);
printf("score : %d\n",hitcount);
}
int main(void) {
char c;
uint8_t move;
rnd_init(clock());//TODO:Clock or millis
tetris_init();
while(true) {
printf("(A,D,W,S) : Move, (Z,C) : SPIN, N : NewGame>");
scanf("%c%*c", &c);
switch(c) {
case 'a'://left
move = MOVE_LEFT;
break;
case 'd'://right
move = MOVE_RIGHT;
break;
case 'w'://up
move = MOVE_UP;
break;
case 's'://down
move = MOVE_DOWN;
break;
case 'z':
move = SPIN_LEFT;
break;
case 'c':
move = SPIN_RIGHT;
break;
case 'n':
tetris_init();
break;
case 'q':
return 0;
default:
move = MOVE_NONE;
break;
}
if(!tetris_update(move)) {
printf("\n\n\n=== GAME OVER ===\nscore : %d\n",hitcount);
//prompt
tetris_init();
}
tetris_print();
tetris_next_print();
tetris_debugprint();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment