Сегодня перед нами стоит задача написать игру крестики-нолики на питоне. Напомним, что крестики-нолики это логическая игра для двух игроков на поле 3х3 клетки.

Для начала зададим поле. Поле у нас будет одномерным списком (list) с числами от 1 до 9. Для создания воспользуемся функцией range()

board = range(1,10)

Теперь напишем функцию, которая будет выводить наше поле в привычном формате.

def draw_board(board):
    print "-------------"
    for i in range(3):
        print "|", board[0+i*3], "|", board[1+i*3], "|", board[2+i*3], "|"
        print "-------------"

Результат выполнения данного кода

результат выполнения функции draw_board

Самое время дать пользователям возможность вводить данные в нашу игру. Пишем функцию take_input

def take_input(player_token):
	valid = False
	while not valid:
		player_answer = raw_input("Куда поставим " + player_token+"? ")
		try:
			player_answer = int(player_answer)
		except:
			print "Некорректный ввод. Вы уверены, что ввели число?"
            continue
		if player_answer >= 1 and player_answer <= 9:
			if (str(board[player_answer-1]) not in "XO"):
				board[player_answer-1] = player_token
				valid = True
			else:
				print "Эта клеточка уже занята"
		else:
			print "Некорректный ввод. Введите число от 1 до 9 чтобы походить."

Как вы видите, функция take_input принимает параметр player_token - крестик или нолик, в зависимости от того, чей сейчас ход. Нам важно ограничить выбор пользователя числами от 1 до 9. Для этого мы используем конструкции try/except и if/else, чтобы удостовериться, что выбранная клеточка не занята. Обратите внимание, что функция take_input не возвращает никакого значения, а только изменяет имеющийся список board.

Осталось написать функцию проверки игрового поля. Назовем эту функцию check_win.

def check_win(board):
	win_coord = ((0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6))
	for each in win_coord:
		if board[each[0]] == board[each[1]] == board[each[2]]:
			return board[each[0]]
	return False 

Проверка результатов игры крестики-нолики достаточно распространенная задача по программированию. Как часто бывает, одно и то же задание в программировании можно решить несколькими способами. В данном случае мы просто создали кортеж (tuple) с выигрышными координатами и прошлись циклом for по нему. Если символы во всех трех заданных клетках равны - возвращаем выигрышный символ, иначе - возвращаем значение False. При этом важно помнить, что непустая строка (наш выигрышный символ) при приведении ее к логическому типу вернет True (это понадобится нам в дальнейшем).

Осталось создать функцию main, в которой мы соберем вместе все описанные функции.

def main(board):
	counter = 0
	win = False
	while not win:
		draw_board(board)
		if counter % 2 == 0:
			take_input("X")
		else:
			take_input("O")
		counter += 1
		if counter > 4:
			tmp = check_win(board)
			if tmp:
				print tmp, "выиграл!"
				win = True
				break
		if counter == 9:
			print "Ничья!"
			break
	draw_board(board)

Работа функции main предельно понятна, разве что строки 45 и 46 могут вызвать непонимание. Мы ждем когда переменная counter станет больше 4 для того, чтобы избежать заведомо ненужного вызова функции check_win (до пятого хода никто точно не может выиграть). Переменная tmp была создана опять же для того, чтобы лишний раз не вызывать функцию check_win, мы просто "запоминаем" ее значение и при необходимости используем на строке 48. Польза от такого подхода не так заметна при работе с небольшими объемами данных, но в целом подобная экономия процессорного времени - хорошая практика.

Теперь мы можем спокойно играть, запустив main(board)

 один из вариантов хода игры крестики-нолики

И последнее уточнение. Для того, чтобы у вас корректно отображались символы кириллицы вставьте следующий код в самое начало вашего файла.

# -*- coding: utf-8 -*-

 Хорошей игры!

PS. Исходный код игры крестики-нолики на Python 3 на github