Power Appsゲームアプリリバーシ

【仕様紹介編】Power AppsでChatGPTと協力してリバーシを作ってみた

Power Apps

コルネさん主催のイベント、【JPPGB】ゲーム作成コンテスト #0のエントリー作品として、Power Appsでリバーシを作成しました。

アイディア賞を受賞しました!コルネさんをはじめとする運営のみなさまありがとうございました!

今回は、作成したリバーシアプリの仕様を紹介します。

【制作過程編】Power AppsでChatGPTと協力してリバーシを作ってみた

ローカル対戦機能

ローカルプレイの画面です。一手戻ったり、リセットすることが可能です。

CPU対戦機能の実装

開放度理論を駆使して攻めてくるCPUです。3連敗しました。

オンライン対戦機能

SharePointリストに値を保持させることで、オンライン対戦を可能にしました。

タイマーを使用して、Refresh関数で1秒ごとにSharePointリストを更新しています。

リバーシのゲームロジック

主要なロジックはボタンにまとめ、Select関数で呼び出す処理にしました。

ボードの初期化 (ButtonReset)

ゲーム開始時の処理およびオンラインプレイ以外に押すことのできるボタンです。

ColBoardをギャラリーに表示して盤面を再現しています。

ターン終了時の処理(ButtonTurnEnd)

Visible = falseです。

置ける場所の定義と、各方向ごとに置いた場所によってひっくり返せる石を定義しています。

相手の色の石が置かれているマスに絞って処理を行うことで、処理量の削減をしています。

上方向のコードのみ考え適宜修正し、他の方向はChatGPTに指示してコードを書いてもらいました。

WhileとExitが使えないため、ForAll関数とCollect関数でどうにかしました。

Concurrent(
    //方向別コレクション初期化
    Clear(ColCanPlace),
    Clear(ColTempUp),
    Clear(ColTempDown),
    Clear(ColTempLeft),
    Clear(ColTempRight),
    Clear(ColTempUpLeft),
    Clear(ColTempUpRight),
    Clear(ColTempDownLeft),
    Clear(ColTempDownRight),
    //ColBoardのCanPlaceを全てfalse
    UpdateIf(
        ColBoard,
        true,
        {CanPlace:false}
    )
);
//隣接マスをColCanPlaceに格納
ForAll(
    Filter(
        ColBoard,
        Color <> RGBA(0, 0, 0, 0),
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))        
    ) As Placed,
    ForAll(
        ColDirections As Direction,
        If(
            ComponentLogic_1.GetTile(Placed.Row + Direction.Row, Placed.Column + Direction.Col, ColBoard).Color = RGBA(0, 0, 0, 0),
            Collect(
                ColCanPlace,
                ComponentLogic_1.GetTile(Placed.Row + Direction.Row, Placed.Column + Direction.Col, ColBoard)
            )
        )
    )
);
// 上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUp, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUp, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUp, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUp, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDown, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDown, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDown, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDown, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUpLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUpLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUpLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUpLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUpRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUpRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUpRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUpRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDownLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    CanPlaceTile.Row + Val.Value <= 7,
                    CanPlaceTile.Column - Val.Value >= 0
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDownLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDownLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDownLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDownRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDownRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDownRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDownRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
)

CPUの処理(ButtonCPU)

Visible = falseです。

開放度理論を用いた重みづけのロジックを実装しました。処理が早く、そこそこ強いです。

無限ループになるためSelect関数が利用できませんでした。コンポーネント化しておけばよかったです。

//Select(ButtonTurnEnd)の処理
Concurrent(
    //方向別コレクション初期化
    Clear(ColCanPlace),
    Clear(ColTempUp),
    Clear(ColTempDown),
    Clear(ColTempLeft),
    Clear(ColTempRight),
    Clear(ColTempUpLeft),
    Clear(ColTempUpRight),
    Clear(ColTempDownLeft),
    Clear(ColTempDownRight),
    //ColBoardのCanPlaceを全てfalse
    UpdateIf(
        ColBoard,
        true,
        {CanPlace:false}
    )
);
//隣接マスをColCanPlaceに格納
ForAll(
    Filter(
        ColBoard,
        Color <> RGBA(0, 0, 0, 0),
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))        
    ) As Placed,
    ForAll(
        ColDirections As Direction,
        If(
            ComponentLogic_1.GetTile(Placed.Row + Direction.Row, Placed.Column + Direction.Col, ColBoard).Color = RGBA(0, 0, 0, 0),
            Collect(
                ColCanPlace,
                ComponentLogic_1.GetTile(Placed.Row + Direction.Row, Placed.Column + Direction.Col, ColBoard)
            )
        )
    )
);
// 上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUp, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUp, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUp, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUp, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUp, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDown, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDown, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDown, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDown, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDown, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUpLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUpLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUpLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUpLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUpLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右上方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row - Val.Value, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempUpRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempUpRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempUpRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempUpRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempUpRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 左下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column - Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDownLeft, CPTValue = CanPlaceTile.Value)).IsStop,
                    CanPlaceTile.Row + Val.Value <= 7,
                    CanPlaceTile.Column - Val.Value >= 0
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDownLeft, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDownLeft, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDownLeft, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDownLeft, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
// 右下方向
ForAll(
    ColCanPlace As CanPlaceTile,
    ForAll(
        Sequence(7) As Val,
        With(
            ComponentLogic_1.GetTile(CanPlaceTile.Row + Val.Value, CanPlaceTile.Column + Val.Value, ColBoard) As ThisTile,
            If(
                And(
                    !Last(Filter(ColTempDownRight, CPTValue = CanPlaceTile.Value)).IsStop,
                    !IsBlank(ThisTile)
                ),
                If(
                    ThisTile.Color = RGBA(0, 0, 0, 0), // 空白ならCanFlipをfalseにしてループ終了
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    UpdateIf(ColTempDownRight, CPTValue = CanPlaceTile.Value, {CanFlip: false}),
                    
                    ThisTile.Color <> _PlayerColor, // 違う色ならコレクションに格納して継続
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: false, CanFlip: false}),
                    
                    ThisTile.Color = _PlayerColor, // 同じ色なら
                    Collect(ColTempDownRight, {CPTValue: CanPlaceTile.Value, CFTValue: ThisTile.Value, IsStop: true, CanFlip: false});
                    // 間に違う色がある場合はCanPlaceとCanFlipをtrueにする
                    If(
                        !IsEmpty(Filter(ColTempDownRight, CPTValue = CanPlaceTile.Value, !IsStop)),
                        UpdateIf(ColBoard, Value = CanPlaceTile.Value, {CanPlace: true });
                        UpdateIf(ColTempDownRight, CPTValue = CanPlaceTile.Value, {CanFlip: true})
                    )
                )
            )
        )
    )
);
//Weight列を追加したColCPU列の定義
ClearCollect(
    ColCPU,
    AddColumns(
        Filter(ColBoard, CanPlace),
        "Weight",
        Value(Not(Value in [1, 9, 8, 15, 14, 6, 48 , 49, 57, 54, 55, 62])) * 10
    )
);
//開放度理論で重みづけ
ForAll(
    Filter(ColBoard, CanPlace) As Board,
    ForAll(
        ColDirections As Direction,
        If(
            ComponentLogic_1.GetTile(Board.Row + Direction.Row, Board.Column + Direction.Col, ColBoard).Color = RGBA(0, 0, 0, 0),
            With(
                LookUp(
                    ColCPU,
                    Value = Board.Value
                ),
                Patch(
                    ColCPU,
                    ThisRecord,
                    {Weight:ThisRecord.Weight - 1}
                )
            )
        )
    )
);
UpdateIf(ColCPU, Value in [0, 7, 56, 63], {Weight:100});
//ColBoardを一番重みが大きいマスで更新
With(
    First(DropColumns(SortByColumns(ColCPU,"Weight", SortOrder.Descending),"Weight")) As CPUPlace,
    Patch(
        ColBoard,
        CPUPlace,
        { 
            Value: CPUPlace.Value, 
            Row: CPUPlace.Row, 
            Column: CPUPlace.Column, 
            Color: _PlayerColor
        }
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempUp, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempDown, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempLeft, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempRight, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempUpLeft, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempUpRight, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempDownLeft, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    );
    UpdateIf(
        ColBoard,
        And(
            Value in Filter(ColTempDownRight, CPTValue = CPUPlace.Value, CanFlip).CFTValue,
            Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
        ),
        { Color: _PlayerColor}
    )
);
//プレイヤー交代
Set(_PlayerColor, ComponentLogic_1.GetEnemyColor(_PlayerColor));
Select(ButtonTurnEnd)

盤面 (GalleryBoard)

置いた場所によって事前に処理しておいた各方向のコレクションで石をひっくり返す処理を行います。

If(
    _GameMode <> 3,
    ClearCollect(
        ColPreBoard,
        ColBoard
    ),
    //ターンを相手に渡す
    With(
        LookUp(ReversiOnline, ID = _SessionID) As Session,
        Patch(
            ReversiOnline,
            Session,
            {PlaceValue: ThisItem.Value, Turn:If(TextInputUser.Text = Session.BlackUser, Session.WhiteUser, Session.BlackUser)}
        )
    )
);
Patch(
    ColBoard,
    ThisItem,
    {Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempUp, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempDown, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempLeft, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempRight, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempUpLeft, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempUpRight, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempDownLeft, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
UpdateIf(
    ColBoard,
    And(
        Value in Filter(ColTempDownRight, CPTValue = ThisItem.Value, CanFlip).CFTValue,
        Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
    ),
    { Color: _PlayerColor}
);
//プレイヤー交代
Set(_PlayerColor, ComponentLogic_1.GetEnemyColor(_PlayerColor));
//CPU個別処理
If(
    _GameMode = 2,
    Select(ButtonCPU),
    Select(ButtonTurnEnd)
)

オンラインプレイ時に相手からターンを渡された時の処理

Visible = falseです。

Defaultプロパティに自分のターンかどうかの判別式を入れているので、自分のターンになるとOnCheckの数式が発動します。

With(
    LookUp(ReversiOnline, _SessionID = ID) As Session,
    If(
        !IsBlank(Session.PlaceValue),
        If(
            LookUp(ColBoard, Value = Session.PlaceValue, Color) = RGBA(0, 0, 0, 0),
            Refresh(ReversiOnline);
            UpdateIf(
                ColBoard,
                Value = Session.PlaceValue,
                {Color:_PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempUp, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempDown, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempLeft, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempRight, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempUpLeft, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempUpRight, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempDownLeft, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            UpdateIf(
                ColBoard,
                And(
                    Value in Filter(ColTempDownRight, CPTValue = Session.PlaceValue, CanFlip).CFTValue,
                    Color = If(_PlayerColor = RGBA(0, 0, 0, 1), RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 1))
                ),
                { Color: _PlayerColor}
            );
            //プレイヤー交代
            Set(_PlayerColor, ComponentLogic_1.GetEnemyColor(LookUp(ColBoard, Value = Session.PlaceValue, Color)));,
            //相手がパスした時
            Set(_PlayerColor, LookUp(ColBoard, Value = Session.PlaceValue, Color))
        );
        Select(ButtonTurnEnd)
    )
)

コンポーネント

コンポーネントを2つ作成しました。共通化はあまり考えなかったです。

GetTile:行、列を入力するとそのタイルの情報を取得できる関数

GetEnemyColor:黒を入力すると白が、白を入力すると黒が返ってくる関数

終わりに

素敵なイベントを企画・運営していただいた、コルネさんをはじめとするみなさま、ありがとうございました!

ぜひ投票をお願いします。

コメント

タイトルとURLをコピーしました