Last active
March 18, 2021 18:56
-
-
Save shiracamus/d0dca0cc75822eb0834d7a78b643af12 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <time.h> | |
typedef const char *String; | |
typedef int Count; | |
typedef int Pos; | |
typedef int Delta; | |
typedef enum { | |
Koma_FREE = '.', | |
Koma_BLACK = 'X', | |
Koma_WHITE = 'O', | |
Koma_BLACK_GO = 'x', | |
Koma_WHITE_GO = 'o', | |
} Koma; | |
const Koma Koma_PLAYER = Koma_BLACK; | |
const Koma Koma_AI = Koma_WHITE; | |
const Koma Koma_AI_GO = Koma_WHITE_GO; | |
#define Ban_HEIGHT (8) | |
#define Ban_WIDTH (8) | |
typedef struct { | |
Count turn; | |
Koma koma[Ban_HEIGHT][Ban_WIDTH]; | |
} Ban; | |
void Ban_reset(Ban *ban) | |
{ | |
ban->turn = 0; | |
for (Pos y = 0; y < Ban_HEIGHT; y++) { | |
for (Pos x = 0; x < Ban_WIDTH; x++) { | |
ban->koma[y][x] = Koma_FREE; | |
} | |
} | |
ban->koma[3][3] = Koma_WHITE; | |
ban->koma[3][4] = Koma_BLACK; | |
ban->koma[4][3] = Koma_BLACK; | |
ban->koma[4][4] = Koma_WHITE; | |
} | |
void Ban_print(Ban *ban) | |
{ | |
printf("\n \\ "); | |
for (Pos x = 1; x < 9; x++) { | |
printf(" %d ", x); | |
} | |
for (Pos y = 0; y < Ban_HEIGHT; y++) { | |
printf("\n %d ", y + 1); | |
for (Pos x = 0; x < Ban_WIDTH; x++) { | |
printf(" %c ", ban->koma[y][x]); | |
} | |
} | |
printf("\n\n"); | |
} | |
bool Ban_is_free(Ban *ban, Pos x, Pos y) | |
{ | |
return ban->koma[y][x] == Koma_FREE; | |
} | |
Count Ban_count_reversible(Ban *ban, Pos x, Pos y, Delta dx, Delta dy, Koma koma) | |
{ | |
if (dx == 0 && dy == 0) | |
return 0; | |
for (Count count = 0;; count++) { | |
y += dy; | |
x += dx; | |
if (x < 0 || x >= Ban_WIDTH || y < 0 || y >= Ban_HEIGHT) | |
return 0; | |
Koma ban_koma = ban->koma[y][x]; | |
if (ban_koma == Koma_FREE) | |
return 0; | |
if (ban_koma == Koma_BLACK_GO) | |
ban_koma = Koma_BLACK; | |
else if (ban_koma == Koma_WHITE_GO) | |
ban_koma = Koma_WHITE; | |
if (ban_koma == koma) | |
return count; | |
} | |
} | |
Count Ban_sum_reversible(Ban *ban, Pos x, Pos y, Koma koma) | |
{ | |
if (!Ban_is_free(ban, x, y)) | |
return 0; | |
Count reversible = 0; | |
for (Delta dy = -1; dy <= 1; dy++) { | |
for (Delta dx = -1; dx <= 1; dx++) { | |
reversible += Ban_count_reversible(ban, x, y, dx, dy, koma); | |
} | |
} | |
return reversible; | |
} | |
void Ban_put_and_reverse(Ban *ban, Pos x, Pos y, Koma koma) | |
{ | |
printf("縦,横: %d,%d\n", y + 1, x + 1); | |
ban->koma[y][x] = koma; | |
for (Delta dy = -1; dy <= 1; dy++) { | |
for (Delta dx = -1; dx <= 1; dx++) { | |
Count reversible = Ban_count_reversible(ban, x, y, dx, dy, koma); | |
for (Delta d = 1; d <= reversible; d++) { | |
Koma *ban_koma = &ban->koma[y + dy * d][x + dx * d]; | |
if (*ban_koma == Koma_BLACK_GO || *ban_koma == Koma_WHITE_GO) | |
continue; // 碁石はひっくり返さない(同じ色のまま) | |
*ban_koma = koma; | |
} | |
} | |
} | |
} | |
bool Ban_put_if_reversible(Ban *ban, Pos x, Pos y, Koma koma) | |
{ | |
if (!Ban_is_free(ban, x, y)) | |
return false; | |
if (Ban_sum_reversible(ban, x, y, koma) == 0) | |
return false; | |
Ban_put_and_reverse(ban, x, y, koma); | |
return true; | |
} | |
typedef struct { | |
Count free; | |
Count player; | |
Count ai; | |
} BanCounter; | |
void BanCounter_count(BanCounter *count, Ban *ban) | |
{ | |
count->free = 0; | |
count->player = 0; | |
count->ai = 0; | |
for (Pos y = 0; y < Ban_HEIGHT; y++) { | |
for (Pos x = 0; x < Ban_WIDTH; x++) { | |
Koma koma = ban->koma[y][x]; | |
if (koma == Koma_BLACK_GO) | |
koma = Koma_BLACK; | |
if (koma == Koma_WHITE_GO) | |
koma = Koma_WHITE; | |
if (koma == Koma_PLAYER) | |
count->player++; | |
else if (koma == Koma_AI) | |
count->ai++; | |
else | |
count->free++; | |
} | |
} | |
} | |
void shougaku_play(Ban *ban) | |
{ | |
printf("小学生の手番です。\n"); | |
for (Count retry = 0; retry < 10; retry++) { | |
Pos x = rand() % Ban_WIDTH; | |
Pos y = rand() % Ban_HEIGHT; | |
if (Ban_put_if_reversible(ban, x, y, Koma_AI)) | |
return; //挟めた | |
if (Ban_is_free(ban, x, y)) { | |
Ban_put_and_reverse(ban, x, y, Koma_AI); //挟めてなくても置く | |
return; | |
} | |
} | |
printf("小学生は置ける場所を見つけられなかったようです。\n"); | |
} | |
void tyuugaku_play(Ban *ban) | |
{ | |
printf("中学生の手番です。\n"); | |
for (Count retry = 0;; retry++) { | |
Pos x = rand() % Ban_WIDTH; | |
Pos y = rand() % Ban_HEIGHT; | |
if (Ban_put_if_reversible(ban, x, y, Koma_AI)) | |
return; | |
if (retry == 1000) { | |
printf("中学生は迷っているようです。\n"); | |
} else if (retry > 2000) { //数値に特別な意味はない | |
if (Ban_is_free(ban, x, y)) { | |
printf("中学生は無理矢理駒を置きました。\n"); | |
Ban_put_and_reverse(ban, x, y, Koma_AI); //挟めてなくても置く | |
return; | |
} | |
} | |
} | |
} | |
void koukou_play(Ban *ban) | |
{ | |
printf("高校生の手番です。\n"); | |
struct { Count count, x, y; } best = { 0, 0, 0 }; | |
for (Pos y = 0; y < Ban_HEIGHT; y++) { | |
for (Pos x = 0; x < Ban_WIDTH; x++) { | |
Count count = Ban_sum_reversible(ban, x, y, Koma_AI); | |
if (best.count < count) { | |
best.count = count; | |
best.x = x; | |
best.y = y; | |
} | |
} | |
} | |
if (best.count > 0) | |
Ban_put_and_reverse(ban, best.x, best.y, Koma_AI); | |
else | |
printf("高校生はパスを選択しました。\n"); | |
} | |
void daigaku_play(Ban *ban) | |
{ | |
printf("大学生の手番です。\n"); | |
for (Count retry = 0;; retry++) { | |
Pos x = rand() % Ban_WIDTH; | |
Pos y = rand() % Ban_HEIGHT; | |
if (Ban_put_if_reversible(ban, x, y, Koma_AI)) { | |
if (ban->turn == 19 || ban->turn == 20) { // 大学生の10手目のとき | |
printf("どこからか囲碁の駒を持ってきました。" | |
"ひっくり返しても色が同じです。\n"); | |
ban->koma[y][x] = Koma_AI_GO; | |
} | |
return; | |
} | |
if (Ban_is_free(ban, x, y)) { | |
if (retry == 5000) { | |
printf("大学生はトイレに行っています。\n"); | |
} else if (retry == 10000) { //数値に特別な意味はない | |
printf("酔った勢いで適当に駒を置きました。\n"); | |
Ban_put_and_reverse(ban, x, y, Koma_AI); //挟めてなくても置く | |
return; | |
} | |
} else { | |
if (retry == 5000) { | |
printf("お酒を飲み過ぎて気持ち悪そうです。\n"); | |
} else if (retry == 10000) { //数値に特別な意味はない | |
printf("酔っていて正常な判断ができないようです。\n"); | |
Ban_put_and_reverse(ban, x, y, Koma_AI); //挟めてなくても置く | |
return; | |
} | |
} | |
} | |
} | |
typedef void (*Play)(Ban *ban); | |
typedef struct { | |
const String welcome; | |
const String perfect; | |
const Play play; | |
} Ai; | |
Ai Ai_shougaku = { | |
"小学生と対戦します。", | |
"全て小学生の駒になりました。あなたは小学生以下です。", | |
shougaku_play, | |
}; | |
Ai Ai_tyuugaku = { | |
"中学生と対戦します。", | |
"全て中学生の駒になりました。完敗です。", | |
tyuugaku_play, | |
}; | |
Ai Ai_koukou = { | |
"高校生と対戦します。", | |
"全て高校生の駒になりました。あなたの負けです。", | |
koukou_play, | |
}; | |
Ai Ai_daigaku = { | |
"大学生が乱入してきました。", | |
"全て大学生の駒になりました。乾杯です。", | |
daigaku_play, | |
}; | |
Ai *Ai_select() | |
{ | |
for (;;) { | |
printf("1:小学生\n2:中学生\n3:高校生\n" | |
"対戦相手の番号を選択してください: "); | |
int a; | |
if (scanf("%d", &a) == 1) { | |
if (a == 1) return &Ai_shougaku; | |
if (a == 2) return &Ai_tyuugaku; | |
if (a == 3) return &Ai_koukou; | |
return &Ai_daigaku; | |
} | |
scanf("%*[^\n]"); // エラー入力内容を読み捨てる | |
} | |
} | |
Pos Player_input(String prompt) | |
{ | |
for (;;) { | |
printf("%s:", prompt); | |
Pos pos; | |
if (scanf("%d", &pos) == 1 && 0 <= pos && pos <= 8) { | |
return pos; | |
} | |
scanf("%*[^\n]"); // エラー入力内容を読み捨てる | |
printf("0あるいは1~8の数字を打ち込んでください:"); | |
} | |
} | |
void Player_play(Ban *ban) | |
{ | |
printf("あなたの手番です。あなたの色は「%c」です。\n", Koma_PLAYER); | |
for (;;) { | |
printf("駒を置く縦の数字と横の数字を入れてください。\n"); | |
Pos y = Player_input("パスをする場合0を入れてください。\n縦"); | |
if (y == 0) { | |
printf("あなたはパスを選択しました。\n"); | |
return; | |
} | |
Pos x = Player_input("縦の数字を間違えた場合は0を入れてください。\n横"); | |
if (x == 0) { | |
printf("縦の数字から入力し直してください。\n"); | |
continue; | |
} | |
y--, x--; | |
if (!Ban_is_free(ban, x, y)) { | |
printf("既に駒が置かれています。もう一度\n"); | |
continue; | |
} | |
if (Ban_put_if_reversible(ban, x, y, Koma_PLAYER)) | |
return; | |
printf("その場所には置けません。もう一度\n"); | |
} | |
} | |
bool Game_is_over(Ban *ban, Ai *ai) | |
{ | |
BanCounter count; | |
BanCounter_count(&count, ban); | |
if (count.ai == 0) { | |
printf("\nあなたの完全勝利です。おめでとうございます。\n"); | |
return true; | |
} | |
if (count.player == 0) { | |
printf("\n%s\n", ai->perfect); | |
return true; | |
} | |
if (count.free == 0) { | |
Delta diff = count.player - count.ai; | |
if (diff == 0) { | |
printf("\n引き分け!\n" | |
"オセロに運を使い果たしてしまったのでは?\n"); | |
} else if (diff > 0) { | |
printf("\n%d差で勝利!\n" | |
"明日はきっといいことがあります。\n", | |
diff); | |
} else { | |
printf("\n%d差で負け!\n" | |
"どうして負けたか明日までに考えておいてください。\n", | |
-diff); | |
} | |
return true; | |
} | |
return false; | |
} | |
int main() | |
{ | |
srand((unsigned int)time(NULL)); | |
Ai *ai = Ai_select(); | |
printf("\n%s\n", ai->welcome); | |
Ban ban; | |
Ban_reset(&ban); | |
Ban_print(&ban); | |
for (ban.turn = 0; !Game_is_over(&ban, ai); ban.turn++) { | |
if (ban.turn % 2 == 0) | |
Player_play(&ban); | |
else | |
ai->play(&ban); | |
Ban_print(&ban); | |
} | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
class Koma: | |
FREE = '.' | |
BLACK = 'X' | |
WHITE = 'O' | |
BLACK_GO = 'x' | |
WHITE_GO = 'o' | |
PLAYER = BLACK | |
AI = WHITE | |
AI_GO = WHITE_GO | |
class Ban: | |
HEIGHT = 8 | |
WIDTH = 8 | |
def reset(ban): | |
ban.turn = 0 | |
ban.koma = [[Koma.FREE for x in range(Ban.WIDTH)] | |
for y in range(Ban.HEIGHT)] | |
ban.koma[3][3] = Koma.WHITE | |
ban.koma[3][4] = Koma.BLACK | |
ban.koma[4][3] = Koma.BLACK | |
ban.koma[4][4] = Koma.WHITE | |
def print(ban): | |
print("\n \\", *[f" {x + 1}" for x in range(Ban.WIDTH)]) | |
for y in range(Ban.HEIGHT): | |
print(f" {y + 1}", | |
*[f" {ban.koma[y][x]}" for x in range(Ban.WIDTH)]) | |
print() | |
def is_free(ban, x, y): | |
return ban.koma[y][x] == Koma.FREE | |
def count_reversible(ban, x, y, dx, dy, koma): | |
if dx == 0 and dy == 0: | |
return 0 | |
for count in range(max(Ban.WIDTH, Ban.HEIGHT)): | |
y += dy | |
x += dx | |
if x < 0 or x >= Ban.WIDTH or y < 0 or y >= Ban.HEIGHT: | |
return 0 | |
ban_koma = ban.koma[y][x] | |
if ban_koma == Koma.FREE: | |
return 0 | |
if ban_koma == Koma.BLACK_GO: | |
ban_koma = Koma.BLACK | |
elif ban_koma == Koma.WHITE_GO: | |
ban_koma = Koma.WHITE | |
if ban_koma == koma: | |
return count | |
def sum_reversible(ban, x, y, koma): | |
if not ban.is_free(x, y): | |
return 0 | |
reversible = 0 | |
for dy in -1, 0, 1: | |
for dx in -1, 0, 1: | |
reversible += ban.count_reversible(x, y, dx, dy, koma) | |
return reversible | |
def put_and_reverse(ban, x, y, koma): | |
print(f"縦,横: {y+1},{x + 1}") | |
ban.koma[y][x] = koma | |
for dy in -1, 0, 1: | |
for dx in -1, 0, 1: | |
reversible = ban.count_reversible(x, y, dx, dy, koma) | |
for d in range(1, reversible + 1): | |
ban_koma = ban.koma[y + dy * d][x + dx * d] | |
if ban_koma in (Koma.BLACK_GO, Koma.WHITE_GO): | |
continue # 碁石はひっくり返さない(同じ色のまま) | |
ban.koma[y + dy * d][x + dx * d] = koma | |
def put_if_reversible(ban, x, y, koma): | |
if not ban.is_free(x, y): | |
return False | |
if ban.sum_reversible(x, y, koma) == 0: | |
return False | |
ban.put_and_reverse(x, y, koma) | |
return True | |
class BanCounter: | |
def count(counter, ban): | |
counter.free = 0 | |
counter.player = 0 | |
counter.ai = 0 | |
for y in range(ban.HEIGHT): | |
for x in range(ban.WIDTH): | |
koma = ban.koma[y][x] | |
if koma == Koma.BLACK_GO: | |
koma = Koma.BLACK | |
if koma == Koma.WHITE_GO: | |
koma = Koma.WHITE | |
if koma == Koma.PLAYER: | |
counter.player += 1 | |
elif koma == Koma.AI: | |
counter.ai += 1 | |
else: | |
counter.free += 1 | |
def shougaku_play(ban): | |
print("小学生の手番です。") | |
for retry in range(10): | |
x = random.randrange(Ban.WIDTH) | |
y = random.randrange(Ban.HEIGHT) | |
if ban.put_if_reversible(x, y, Koma.AI): | |
return # 挟めた | |
if ban.is_free(x, y): | |
ban.put_and_reverse(x, y, Koma.AI) # 挟めてなくても置く | |
return | |
print("小学生は置ける場所を見つけられなかったようです。") | |
def tyuugaku_play(ban): | |
print("中学生の手番です。") | |
for retry in range(2000): | |
x = random.randrange(Ban.WIDTH) | |
y = random.randrange(Ban.HEIGHT) | |
if ban.put_if_reversible(x, y, Koma.AI): | |
return | |
if retry == 1000: | |
print("中学生は迷っているようです。") | |
print("中学生は無理矢理駒を置きました。") | |
ban.put_and_reverse(x, y, Koma.AI) # 挟めてなくても置く | |
def koukou_play(ban): | |
print("高校生の手番です。") | |
class best: | |
count, x, y = 0, 0, 0 | |
for y in range(Ban.HEIGHT): | |
for x in range(Ban.WIDTH): | |
count = ban.sum_reversible(x, y, Koma.AI) | |
if best.count < count: | |
best.count = count | |
best.x = x | |
best.y = y | |
if best.count > 0: | |
ban.put_and_reverse(best.x, best.y, Koma.AI) | |
else: | |
print("高校生はパスを選択しました。") | |
def daigaku_play(ban): | |
print("大学生の手番です。") | |
for retry in range(10000): | |
x = random.randrange(Ban.WIDTH) | |
y = random.randrange(Ban.HEIGHT) | |
if ban.put_if_reversible(x, y, Koma.AI): | |
if ban.turn in (19, 20): # 大学生の10手目のとき | |
print("どこからか囲碁の駒を持ってきました。" | |
"ひっくり返しても色が同じです。") | |
ban.koma[y][x] = Koma.AI_GO | |
return | |
if ban.is_free(x, y): | |
if retry == 5000: | |
print("大学生はトイレに行っています。") | |
else: | |
if retry == 5000: | |
print("お酒を飲み過ぎて気持ち悪そうです。") | |
if ban.is_free(x, y): | |
print("酔った勢いで適当に駒を置きました。") | |
ban.put_and_reverse(x, y, Koma.AI) # 挟めてなくても置く | |
else: | |
print("酔っていて正常な判断ができないようです。") | |
ban.put_and_reverse(x, y, Koma.AI) # 挟めてなくても置く | |
class Ai: | |
def __init__(ai, welcome, perfect, play): | |
ai.welcome = welcome | |
ai.perfect = perfect | |
ai.play = play | |
Ai_shougaku = Ai( | |
"小学生と対戦します。", | |
"全て小学生の駒になりました。あなたは小学生以下です。", | |
shougaku_play, | |
) | |
Ai_tyuugaku = Ai( | |
"中学生と対戦します。", | |
"全て中学生の駒になりました。完敗です。", | |
tyuugaku_play, | |
) | |
Ai_koukou = Ai( | |
"高校生と対戦します。", | |
"全て高校生の駒になりました。あなたの負けです。", | |
koukou_play, | |
) | |
Ai_daigaku = Ai( | |
"大学生が乱入してきました。", | |
"全て大学生の駒になりました。乾杯です。", | |
daigaku_play, | |
) | |
def Ai_select(): | |
print("1:小学生\n2:中学生\n3:高校生\n" | |
"対戦相手の番号を選択してください: ") | |
a = input().strip() | |
if a == "1": return Ai_shougaku | |
if a == "2": return Ai_tyuugaku | |
if a == "3": return Ai_koukou | |
return Ai_daigaku | |
class Player: | |
def input(prompt): | |
while True: | |
try: | |
return int(input(f"{prompt}:")) | |
except ValueError: | |
print("0あるいは1~8の数字を打ち込んでください:") | |
def play(ban): | |
print(f"あなたの手番です。あなたの色は「{Koma.PLAYER}」です。") | |
while True: | |
print("駒を置く縦の数字と横の数字を入れてください。") | |
y = Player.input("パスをする場合0を入れてください。\n縦") | |
if y == 0: | |
print("あなたはパスを選択しました。") | |
return | |
x = Player.input("縦の数字を間違えた場合は0を入れてください。\n横") | |
if x == 0: | |
print("縦の数字から入力し直してください。") | |
continue | |
y -= 1 | |
x -= 1 | |
if not ban.is_free(x, y): | |
print("既に駒が置かれています。もう一度") | |
continue | |
if ban.put_if_reversible(x, y, Koma.PLAYER): | |
return | |
print("その場所には置けません。もう一度") | |
class Game: | |
def is_over(ban, ai): | |
counter = BanCounter() | |
counter.count(ban) | |
if counter.ai == 0: | |
print("\nあなたの完全勝利です。おめでとうございます。") | |
return True | |
if counter.player == 0: | |
print(f"\n{ai.perfect}") | |
return True | |
if counter.free == 0: | |
diff = counter.player - counter.ai | |
if diff == 0: | |
print("\n引き分け!\n" | |
"オセロに運を使い果たしてしまったのでは?") | |
elif diff > 0: | |
print(f"\n{diff}差で勝利!\n" | |
"明日はきっといいことがあります。") | |
else: | |
print(f"\n{-diff}差で負け!\n" | |
"どうして負けたか明日までに考えておいてください。") | |
return True | |
return False | |
def main(): | |
ai = Ai_select() | |
print(f"\n{ai.welcome}") | |
ban = Ban() | |
ban.reset() | |
ban.print() | |
while not Game.is_over(ban, ai): | |
if ban.turn % 2 == 0: | |
Player.play(ban) | |
else: | |
ai.play(ban) | |
ban.print() | |
ban.turn += 1 | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment