【至急】DXライブラリでオセロを作って記事にする

:::note info
この記事は[SLP-KBIT AdventCalendar2024](https://adventar.org/calendars/10572)6日目の記事です。
:::

# 目次
 **・はじめに** 
 **・環境構築と基礎** 
 **・作ったもの一覧** 
 **・メイン関数最終形態** 
 **・おわりに** 
 
# はじめに
こんにちは、Advent Calendar 6日目担当のkaedeです。現在時刻は12月6日17時です。完全に忘れていました。何も考えてなかったので最近触っているDXライブラリで簡単にゲームを作ります。
はい、よーいスタート。

# 環境構築や基礎
正直、環境構築はよくわかってません。ですが皆さん[あちら](https://www.ay3s-room.com/entry/dxlib-visual-studio)の方へ行かれるようです。
ゲームの設計については[このサイト](https://dixq.net/g/)が参考になります。[公式リファレンス](https://dxlib.xsrv.jp/dxfunc.html)を見ながら作りましょう。とりあえずメイン関数の骨組みは次の通り。
```c++:main.cpp
#include "DxLib.h"
#include"osero.h"
#include"keyboard.h"
#include<ctime>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    if (ChangeWindowMode(TRUE))return -1;
    SetGraphMode(640, 640, 16);
    if (DxLib_Init())return -1;
    if (SetDrawScreen(DX_SCREEN_BACK))return -1;
    srand((unsigned)time(NULL));

    Osero osero;
    osero.init();
    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
        osero.draw_board();
        osero.move();
        if ( hitkey(KEY_INPUT_RETURN)) {
            osero.put_stone();
        }
    }

    DxLib_End();
    return 0;
}
```
`osero.h`と`keyboard.h`は自分で作る予定のものです。メイン関数の動作は
1. DXライブラリやオセロクラスの初期化
2. ゲームボードの表示
3. プレーヤーの操作を受け付ける
4. 3を受けて必要な処理を実行する
5. 4の結果を表示(=2に戻る)

このような流れになる予定です。
それでは実装してきます。

# 作ったもの一覧
実装してきました。現在時刻は21時ぐらいです。4時間ほどでだいたい作れたのでまとめて紹介します。

## keyboard.h
DXライブラリにはキーボード入力回数を扱う関数はないため自作する必要があります。ということで作ったものがこちら。`KeyCode`はDXライブラリで定義されているマクロです。
```c++:hitkey.cpp
#include"DxLib.h"
#include"keyboard.h"

bool hitkey(int KeyCode) {
	bool f = false;
	while (CheckHitKey(KeyCode))f = true;
	return f;
}
```

## osero.h
多いので関数の実装はほとんど省略します。汚コードを見る覚悟の準備ができた方は[こちら](https://github.com/kaede9030/Advent-Calendar-)も参考にしてください。
```c++:osero.h
#ifndef OSERO_H
#define OSERO_H

typedef struct {//座標
	int y;
	int x;
}P;

class Osero {
public:
	int board[8][8];//i行j列の石の色(-1, 0, 1)
	int color[3];//(0, 1, 2) = (黒, 緑, 白)
	int dx[8] = { 0,1,1,1,0,-1,-1,-1 };//xの8方向
	int dy[8] = { -1,-1,0,1,1,1,0,-1 };//yの8方向
	int turn = 1;//現在の手番色
	int pturn, cturn;//プレーヤー、コンピューターの手番色
	P p;//カーソル
	void init();
	void draw_board();
	void move();
	void put_stone();
	bool check_stone(int y, int x);
	void res();
};

#endif

```
きたないですね。各関数の簡単な説明を書いておきます。

```c++:init.cpp
void init();//board, color, p, turnの初期化
```

```c++:draw_board.cpp
void draw_board() {
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			//緑の正方形を間をあけて描写
			DrawBox(j * 80 + 1, i * 80 + 1, j * 80 + 80, i * 80 + 80, color[1], TRUE);
			//黒, 緑, 白の石を描写
			DrawCircle(j * 80 + 40, i * 80 + 40, 30, color[board[i][j]+1], TRUE);
		}
	}
	//プレイヤーのカーソル(手番色の小さい正方形)を描写
	DrawBox(p.x * 80 + 3, p.y * 80 + 3, p.x * 80 + 15, p.y * 80 + 15, color[pturn+1], TRUE);
}
```
この関数ではゲームボード、石、プレイヤーのカーソルを描写します(下画像参照)。
現在の手番と色の番号を1(白)、-1(黒)で表現しているため、`color`にアクセスするときは`board+1`しています。
手抜きポイントとして、マスの境界線は描写していません。画像の境界線は黒い線や長方形ではなく、何も描写されてないだけです。
![スクリーンショット 2024-12-11 200243.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640527/0c6817a9-a2e9-4d6a-dc68-f914294ee9fb.png)


```c++:move.cpp
void move();//上下キーで移動、ゲームボード外へ行かないように調整
```

```c++:put_stone.cpp
void put_stone();//check_stone()で置けるか確認し、boardとturnを更新
```

```c++:check_stone.cpp
bool check_stone(int y,int x);//y行x列マスに石を置けるならtrue
```

```c++:res.cpp
void res();//結果発表～～
```

# メイン関数最終形態
上で用意したものを使って最終的にこんなものができました。
```c++:main.cpp
#include "DxLib.h"
#include"osero.h"
#include"keyboard.h"
#include<ctime>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    if (ChangeWindowMode(TRUE))return -1;
    SetGraphMode(640, 640, 16);
    if (DxLib_Init())return -1;
    if (SetDrawScreen(DX_SCREEN_BACK))return -1;
    srand((unsigned)time(NULL));

    Osero osero;
    osero.init();
    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
        osero.draw_board();
        osero.move();
        if ( hitkey(KEY_INPUT_RETURN)) {
            osero.put_stone();
        }
        bool f = true;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                if (osero.check_stone(i, j))f = false;
            }
        }
        if (f)break;
    }

    while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
        osero.res();
        if (hitkey(KEY_INPUT_RETURN))break;
    }

    DxLib_End();
    return 0;
}
```
ゲームは次のように進行します。
1. DXライブラリと`Osero`クラスの初期化
2. ゲームボード、石、カーソルの表示
3. 十字キーでカーソルを動かす
4. Enterが押されると次のどちらかを実行
    1. プレーヤーの手番の場合、カーソルがあるマスに石を置こうとする
    1. コンピューターの手番の場合、石を置けるマスからランダムに選択して置こうとする
5. 石を置けなくなっているか調べる
    1. 置ける場合2に戻る
    1. 置けない場合は`break`する
6. 石を数えて勝敗を表示する

# おわりに
現在時刻は22時ぐらいです。ここでタイマーストップ。記録はだいたい5時間でした。完装した感想ですが、とりあえず終わってよかったです。完装したとは言いましたがいろいろ足りてないと思います。後、とにかく汚い。例えば、ここには載せてないですが、`put_stone()`と`check_stone()`はほぼ同じコードを書いているのでもうちょっとやりようがあったと思います。
もういい時間なのでこの記事はおしまいです。ここまで読んでくださりありがとうございました。

--- 
**変更とか**
ゲームボード描写の説明を追記しました。また、[github](https://github.com/kaede9030/Advent-Calendar-)にコードを追加しました。
記事内で"オセロ"を"othello"ではなく"osero"と表記していますが仕様です。