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