2048ゲームのサンプル

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;
}