I saw @ kei011's "○ ✕ Game". The purpose of beginners is to complete a program that works on their own, so it's great to achieve that and post the results.
However, I was disappointed that the program code is common to beginners.
I refactored it while being aware of object orientation. I myself am refactoring through trial and error, so if you have any other good ideas, I would appreciate it if you could comment.
First, list the things and actors that appear in the "○ ✕ game".
Typedef each one.
In addition, I typedefed the string string_t and the position point_t to put the stone.
Here is the refactored C source code.
tictactoe.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#ifdef __GNUC__
#define scanf_s scanf
#endif
#define SIZE (3) //Board size (vertical=side)
typedef enum {
SPACE = '-', //Value when there are no stones on the board
STONE_A = 'A',
STONE_B = 'B',
} stone_t; //Stone to put on the board
typedef stone_t board_t[SIZE][SIZE]; //Board
typedef int point_t; //Board xy position(0 ~ STEP-1)
typedef int step_t; //Update diff
typedef const char *string_t; //String
typedef struct player player_t; //Players (people and computers)
struct player {
string_t name;
stone_t stone;
string_t order;
void (*play)(player_t *player, board_t board);
player_t *opponent;
};
//Board display
void show(board_t board)
{
printf("★ Current table ★\n"
"\n x");
for (point_t x = 0; x < SIZE; x++) {
printf(" %2d", x);
}
printf("\n y -");
for (point_t x = 0; x < SIZE; x++) {
printf("---");
}
for (point_t y = 0; y < SIZE; y++) {
printf("\n%2d| ", y);
for (point_t x = 0; x < SIZE; x++) {
printf(" %c ", board[y][x]);
}
}
printf("\n");
}
//True if there is a place on the board
bool space(board_t board)
{
for (point_t y = 0; y < SIZE; y++) {
for (point_t x = 0; x < SIZE; x++) {
if (board[y][x] == SPACE) {
return true;
}
}
}
return false;
}
//Returns true if the specified stone follows the board and the row is complete
bool follow(board_t board, stone_t stone, point_t y, point_t x, step_t dy, step_t dx)
{
for (step_t i = 1; i < SIZE; i++) {
y = (y + dy + SIZE) % SIZE;
x = (x + dx + SIZE) % SIZE;
if (board[y][x] != stone) {
return false;
}
}
return true;
}
//When the line is completed on the board with the specified stone*py, *Set px and return true
bool line(board_t board, stone_t first, stone_t other, point_t *py, point_t *px) {
const step_t INCRESE_Y = 1, INCRESE_X = 1, DECRESE_X = -1, STAY_Y = 0, STAY_X = 0;
for (point_t y = 0; y <SIZE ; y++) {
for (point_t x = 0; x < SIZE; x++) {
if (board[y][x] == first &&
(follow(board, other, y, x, STAY_Y, INCRESE_X) || //side
follow(board, other, y, x, INCRESE_Y, STAY_X))) { //Vertical
*py = y, *px = x;
return true;
}
}
point_t x = y;
if (board[y][x] == first &&
follow(board, other, y, x, INCRESE_Y, INCRESE_X)) { //Bottom right
*py = y, *px = y;
return true;
}
x = SIZE - 1 - y;
if (board[y][x] == first &&
follow(board, other, y, x, INCRESE_Y, DECRESE_X)) { //Bottom left
*py = y, *px = x;
return true;
}
}
return false;
}
//If you are in reach*px, *Set py and return true
bool reach(board_t board, stone_t stone, point_t *py, point_t *px)
{
return line(board, SPACE, stone, py, px);
}
//Returns true if in bingo state
bool bingo(board_t board, stone_t stone)
{
point_t y, x;
return line(board, stone, stone, &y, &x);
}
//Position input
point_t input(player_t *player, string_t target)
{
printf("%s:%s(%c)of%Enter s:", player->order, player->name, player->stone, target);
point_t point;
switch (scanf_s("%d", &point)) {
case EOF:
exit(1);
case 1:
if (0 <= point && point < SIZE) {
return point;
}
break;
default:
scanf("%*s"); //Discard input
}
printf("The value is incorrect.\n\n");
return -1;
}
//People play(Take a move)
void human(player_t *player, board_t board)
{
while (true) {
point_t x = input(player, "side(x)");
if (x < 0) continue;
point_t y = input(player, "Vertical(y)");
if (y < 0) continue;
if (board[y][x] == SPACE) {
board[y][x] = player->stone;
return;
}
printf("The specified position is incorrect.\n\n");
}
}
//Computer play(Take a move)
void computer(player_t *player, board_t board)
{
point_t y, x;
if (reach(board, player->stone, &y, &x)) {
//Choose your reach
} else if (reach(board, player->opponent->stone, &y, &x)) {
//Interfere with the reach of the opponent
} else {
y = 1, x = 1; //middle
while (board[y][x] != SPACE) {
y = rand() % SIZE, x = 0;
while (x < SIZE - 1 && board[y][x] != SPACE) {
x++;
}
}
}
board[y][x] = player->stone;
}
//SIZE line-up game
void game()
{
player_t player1 = { "you", STONE_A, "First strike", human },
player2 = { "Computer", STONE_B, "Second attack", computer },
*player = &player1;
player1.opponent = &player2;
player2.opponent = &player1;
board_t board;
for (point_t y = 0; y < SIZE; y++) {
for (point_t x = 0; x < SIZE; x++) {
board[y][x] = SPACE;
}
}
show(board);
while (space(board)) {
player->play(player, board);
show(board);
if (bingo(board, player->stone)) {
printf("%s victory!\n", player->name);
return;
}
player = player->opponent;
}
printf("draw!\n");
}
int main(void)
{
srand((unsigned int)time(NULL));
game();
return 0;
}
"Mr. board, are you somewhere?" "Mr. board, are these three stones lined up?" "You, please do one move" "Computer, take a step" Thinking about who you are asking for, that "who" is used as the variable name and the first argument of the function.
I ported the C source code to Python, an object-oriented language. If you can read C language, you can read Python as it is.
"Who" is defined as a class.
Each function is assigned to a class considering who the job is.
Since "who" is "self" when viewed from inside the class, the first argument is the variable name self.
When you want to call a function in a class, write it in the order of "who.function name (argument)", and it will be converted to "function in the corresponding class (who, argument)" and called. .. So, let self be the first argument.
If you have a Python3 interpreter, you can run it by typing python tictactoe.py at the command prompt.
I wanted to keep the C language as it is, so I avoid Python-like writing as much as possible.
tictactoe.py
from random import randint
SPACE = '-' #Value when there are no stones on the board
class Board(list): # typedef char board_t[3][3]; (list is an array)
#Board initialization (special name determined by Python)
def __init__(self):
super().__init__([ [ SPACE, SPACE, SPACE ],
[ SPACE, SPACE, SPACE ],
[ SPACE, SPACE, SPACE ] ])
#Board display
def show(self): # void show(board_t board)
print("★ Current table ★\n\n"
" x 0 1 2\n"
" y -------")
for y in range(3):
print(" %d| %c %c %c" % (y, self[y][0], self[y][1], self[y][2]))
#Return true if there is a place on the board
def space(self): # bool_t space(board_t board)
for y in range(3):
for x in range(3):
if self[y][x] == SPACE:
return True
return False
#If the specified three stones are lined up on the board, the coordinates are returned, if not lined up, None
def line(self, stone1, stone2, stone3): #Python can return multiple values as a return value>Argument reduction with
for y in range(3):
for x in range(3):
if (self[y][x] == stone1 and
((self[y][(x + 1) % 3] == stone2 and #Side by side
self[y][(x + 2) % 3] == stone3) or
(self[(y + 1) % 3][x] == stone2 and #Vertically
self[(y + 2) % 3][x] == stone3))):
return (y, x)
if (self[(y + 0) % 3][(y + 0) % 3] == stone1 and #Bottom right
self[(y + 1) % 3][(y + 1) % 3] == stone2 and
self[(y + 2) % 3][(y + 2) % 3] == stone3):
return (y, y)
if (self[(y + 0) % 3][(2 - y + 3) % 3] == stone1 and #Bottom left
self[(y + 1) % 3][(1 - y + 3) % 3] == stone2 and
self[(y + 2) % 3][(0 - y + 3) % 3] == stone3):
return (y, (2 - y + 3) % 3)
return None
#Returns position if reach, None if not
def reach(self, stone):
return self.line(SPACE, stone, stone)
#Returns true if in bingo state
def bingo(self, stone):
return self.line(stone, stone, stone) is not None
class Player:
def __init__(self, name, stone, order):
self.name = name
self.stone = stone
self.order = order
class Human(Player):
#Position input
def input(self, target):
try:
point = int(input("%s:%s(%c)of%Enter s:" % (self.order, self.name, self.stone, target)))
if 0 <= point <= 2:
return point
except ValueError:
pass
print("The value is incorrect.\n")
return -1
#To play(Take a move)
def play(self, board):
while True:
x = self.input("side(x)")
if x < 0: continue
y = self.input("Vertical(y)")
if y < 0: continue
if board[y][x] == SPACE:
board[y][x] = self.stone
return
print("The specified position is incorrect.\n")
class Computer(Player):
#To play(Take a move)
def play(self, board):
position = board.reach(self.stone) #Your reach choice
if position is None:
position = board.reach(self.opponent.stone) #Interfering with the reach of the other party
if position is None:
y, x = 1, 1 #middle
while board[y][x] != SPACE:
y, x = randint(0, 2), 0
while x < 2 and board[y][x] != SPACE:
x += 1
position = (y, x)
y, x = position
board[y][x] = self.stone
#Tic-tac-toe game
def main():
player1 = Human("you", 'A', "First strike")
player2 = Computer("Computer", 'B', "Second attack")
player = player1
player1.opponent = player2
player2.opponent = player1
board = Board()
board.show()
while board.space():
player.play(board)
board.show()
if board.bingo(player.stone):
print("%s victory!" % player.name)
return
player = player.opponent
print("draw!")
return
if __name__ == '__main__':
main()
Recommended Posts