C++で書かれた2048ゲーム(http://vivi.dyndns.org/tech/games/2048.html)をC言語に書き換えたもの。
コンソールアプリで文字色を変更する処理は省略。
//----------------------------------------------------------------------
// 2048
// Copyright (C) 2048 by N.Tsuda
// License: CDDL 1.0 (http://opensource.org/licenses/CDDL-1.0)
//----------------------------------------------------------------------
#include <conio.h> // _getch
#include <time.h> // time
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WALL -1 // 番人
#define BOARD_WD 4 // ボード幅
#define BOARD_HT 4 // ボード高さ
#define CELL_WIDTH 8 // セル表示幅
#define KEY_ARROW 0xe0
#define KEY_UP 0x48
#define KEY_LEFT 0x4b
#define KEY_RIGHT 0x4d
#define KEY_DOWN 0x50
int g_score = 0;
//int g_nBlank; // 空欄箇所数
int g_board[BOARD_WD+2][BOARD_HT+2]; // 番人付きボード2次元配列
#define swap(a, b) (a += b, b = a - b, a -= b)
// 最上位ビットの位置(0~31)を返す
// v が0ならば0, 1ならば1,2ならば2, 4ならば3, 8ならば4,... を返す
// v がマイナスならば -1 を返す
// int は符号付き32ビット整数と仮定する
int msbPos(int v)
{
int n = 31;
int mask;
if( v == 0 ) return 0;
if( v < 0 ) return -1;
mask = 1 << (n-1);
while( (v & mask) == 0 ) {
mask >>= 1;
--n;
}
return n;
}
//----------------------------------------------------------------------
void init_board()
{
int x, y;
//g_nBlank = BOARD_WD * BOARD_HT; // 空欄箇所数
for (x = 0; x < BOARD_WD+2; ++x) {
for (y = 0; y < BOARD_HT+2; ++y) {
if( x == 0 || x == BOARD_WD + 1 ||
y == 0 || y == BOARD_HT + 1 )
{
g_board[x][y] = WALL;
} else {
g_board[x][y] = 0;
}
}
}
}
void print_line(int y, int printVal)
{
int x, v;
char str[256];
for (x = 1; x <= BOARD_WD; ++x) {
str[0] = '\0';
v = g_board[x][y];
if( printVal ) {
if( !v ) {
strcat_s(str, sizeof(str), " . ");
} else {
char ss[16];
sprintf_s(ss, sizeof(ss), "%4d ", v);
strcat_s(str, sizeof(str), ss);
}
}
printf("%s", str);
}
printf("\n");
}
void print_board()
{
int y;
printf("SCORE: %d\n\n", g_score);
for (y = 1; y <= BOARD_HT; ++y) {
print_line(y, 0);
print_line(y, 0);
print_line(y, 1);
print_line(y, 0);
print_line(y, 0);
}
printf("\n");
}
int nBlank()
{
int x, y;
int nBlank = 0;
for (y = 1; y <= BOARD_HT; ++y) {
for (x = 1; x <= BOARD_WD; ++x) {
if( g_board[x][y] == 0 ) ++nBlank;
}
}
return nBlank;
}
// 空き箇所のどこかに 2(75%) または 4(25%) を配置する
void put_number()
{
int pos, v, x, y;
int n = nBlank();
if( n == 0 ) return;
pos = rand() % n;
v = (rand() % 100) < 75 ? 2 : 4; // 75% の確率で2,25%の確率で4
for (y = 1; y <= BOARD_HT; ++y) {
for (x = 1; x <= BOARD_WD; ++x) {
if( g_board[x][y] == 0 ) { // 空欄を探す
if( !pos ) {
g_board[x][y] = v;
return;
}
--pos;
}
}
}
}
// 盤面左右反転
void hrev_board()
{
int x, y;
for (y = 1; y <= BOARD_HT; ++y) {
for (x = 1; x <= BOARD_WD/2; ++x) {
swap(g_board[x][y], g_board[BOARD_WD + 1 - x][y]);
}
}
}
// x, y 反転
void swapxy_board()
{
int x, y;
for (y = 1; y < BOARD_HT; ++y) {
for (x = y + 1; x <= BOARD_WD; ++x) {
swap(g_board[x][y], g_board[y][x]);
}
}
}
int move_right()
{
int y, src, dst, v;
int moved = 0;
for (y = 1; y <= BOARD_HT; ++y) {
dst = BOARD_WD;
for(src = BOARD_WD; src >= 1; --src) {
v = g_board[src][y];
if( v != 0 ) {
if( src == BOARD_WD ) continue; // 右端の場合
if( g_board[dst][y] == 0 ) { // 移動先が空欄
if( dst != src ) {
g_board[src][y] = 0;
g_board[dst][y] = v;
moved = 1;
}
} else if( g_board[dst][y] == v ) { // 同じ数字
g_score += v * 2;
g_board[src][y] = 0;
g_board[dst][y] += v;
--dst;
moved = 1;
} else {
--dst;
if( dst != src ) {
g_board[src][y] = 0;
g_board[dst][y] = v;
moved = 1;
}
}
}
}
}
return moved;
}
int move_left()
{
int rc;
hrev_board();
rc = move_right();
hrev_board();
return rc;
}
int move_up()
{
int rc;
swapxy_board();
hrev_board();
rc = move_right();
hrev_board();
swapxy_board();
return rc;
}
int move_down()
{
int rc;
swapxy_board();
rc = move_right();
swapxy_board();
return rc;
}
int isMovable()
{
int x, y;
for (y = 1; y <= BOARD_HT; ++y) {
for (x = 1; x <= BOARD_WD; ++x) {
if( g_board[x][y] == 0 ||
g_board[x][y] == g_board[x+1][y] ||
g_board[x][y] == g_board[x][y+1] )
{
return 1;
}
}
}
return 0;
}
int g2048()
{
int moved, c;
moved = 0;
srand((int)time(0));
for (;;) {
g_score = 0;
init_board();
put_number();
for(moved;;) {
if( moved )
put_number();
print_board();
if( !isMovable() ) break;
printf("Type ←↑↓→ ");
c = _getch();
if( c == 'q' ) break;
if( c == KEY_ARROW ) {
c = _getch();
switch( c ) {
case KEY_LEFT:
moved = move_left();
break;
case KEY_RIGHT:
moved = move_right();
break;
case KEY_UP:
moved = move_up();
break;
case KEY_DOWN:
moved = move_down();
break;
}
}
}
printf("Try again ? [y/n] ");
for (;;) {
c = _getch();
if( c == KEY_ARROW )
_getch();
if( c == 'y' || c == 'Y' ||
c == 'n' || c == 'N' )
{
break;
}
}
if( c != 'y' && c != 'Y' ) break;
}
return 0;
}