Skip to content

Instantly share code, notes, and snippets.

@shiracamus
Last active March 18, 2021 18:56
Show Gist options
  • Save shiracamus/d0dca0cc75822eb0834d7a78b643af12 to your computer and use it in GitHub Desktop.
Save shiracamus/d0dca0cc75822eb0834d7a78b643af12 to your computer and use it in GitHub Desktop.
#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;
}
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