Двумерные массивы чаще всего используются для создания игр, использующих поле (матрицу). Мы рассмотрим простейшую программу игры в крестики-нолики.
	Матрица для игры в крестики-нолики имеет вид двумерного массива символов 3 на 3. Пользователь всегда играет крестиками, а компьютер - ноликами. Когда ходит пользователь, «X» помещается в указанную позицию матрицы. Когда наступает очередь ходить компьютеру, он сканирует матрицу и помещает «О» в пустое место матрицы. (Это достаточно тупая игра и можно получить определенное удовольствие, немного ее улучшив.) Если компьютер не может найти пустой ячейки, он выводит результат игры и завершает работу программы. Игровая матрица инициализируется так, что в начале игры она содержала пробелы. Ниже показана программа крестики-нолики:
	
	#include <stdio.h>
	#include <stdlib.h>
	
	/* простая программа игры в крестики-нолики */
	#define SPACE ' '
	char matrix[3][3] = { /* матрица для крестиков-ноликов */
	{SPACE, SPACE, SPACE},
	{SPACE, SPACE, SPACE},
	{SPACE, SPACE, SPACE}
	} ;
	void get_computer_move(void), get_player_move(void);
	void disp_matrix(void);
	char check (void);
	int main()
	{
	char done;
	printf("This is the game of Tic-Tac-Toe.\n");
	printf("You will be playing against the computer.\n");
	done = SPACE;
	do {
	disp_matrix(); /* вывод игровой доски */
	get_player_move(); /* ходит игрок */
	done = check(); /* проверка на победу */
	if (done!=SPACE) break; /* победитель */
	get_computer_move(); /* ходит компьютер */
	done=check(); /* проверка на победу */
	} while(done==SPACE);
	if(done=='X') printf("You won!\n");
	else printf("I won!!!!\n");
	disp_matrix(); /* отображение результирующего положения */
	return 0;
	}
	
	/* ввод хода игрока */
	void get_player_move(void)
	{
	int x, у;
	printf("Enter coordinates for your X.\n");
	printf("Row? ");
	scanf ("%d", &x);
	printf("Column? ");
	scanf("%d", &y);
	х--; y--;
	if (x<0 || y<0 || x>2 || y>2 || matrix[x] [y] !=SPACE)
	{
	printf("Invalid move, try again.\n");
	get_player_move();
	}
	else matrix[x][y]='X';
	}
	
	/* ход компьютера */
	void get_computer_move(void)
	{
	register int t;
	char *p;
	p = (char *) matrix;
	for (t=0; *p!=SPACE && t<9; ++t) p++;
	if(t==9)
	{
	printf("draw\n");
	exit(0); /* game over */
	}
	else *p = 'O';
	}
	
	/* отображение игровой доски */
	void disp_matrix(void)
	{
	int t;
	for(t=0; t<3; t++)
	{
	printf(" %c | %c | %c", matrix[t][0], matrix[t][1], matrix[t][2]);
	if(t!=2) printf("\n-|-|-\n");
	}
	printf("\n");
	}
	
	/* проверка на победу */
	char check(void)
	{
	int t;
	char *p;
	for(t=0; t<3; t++) { /* проверка строк */
	p = &matrix[t] [0];
	if (*p==* (p+1) && * (p+1)==*(p+2)) return *p;
	}
	for(t=0; t<3; t++) { /* проверка столбцов */
	p = &matrix[0][t];
	if(*p==*(p+3) && *(p+3)==*(p+6)) return *p;
	}
	
	/* проверка диагоналей */
	if(matrix[0] [0]==matrix [1] [1] && matrix[1] [1]==matrix [2] [2] )
	return matrix[0][0];
	if(matrix[0][2]==matrix[1][1] && matrix[1][1]==matrix[2] [0])
	return matrix[0][2];
	return SPACE;
	}
Массив инициализируется пробелами, поскольку пробелы используются для отображения вакантного состояния функциями get_player_move() и get_computer_move(). Тот факт, что пробелы используются вместо нулевых символов, упрощает функцию отображения матрицы - disp_matrix(), позволяя выводить содержимое массива на экран без преобразований. Обратим внимание, что процедура get_player_move() вызывает рекурсию в случае ввода неправильного значения. Это пример использования рекурсий для упрощения подпрограммы и уменьшения количества кода, необходимого для реализации функции.
В главном цикле каждый раз при вводе хода вызывается функция check(). Данная функция определяет, выиграна ли игра и, если да, то кем. Функция check() возвращает «X» в случае выигрыша пользователя, или «О», если выиграл компьютер. В противном случае она возвращает пробел. check() сканирует строки, столбцы и диагонали с целью поиска выигрышной конфигурации.
Подпрограммы в данном примере по-разному осуществляют доступ к массиву matrix. Следует изучить их все, чтобы убедиться в понимании каждой операции с массивом.
