ド素人がFlutterでオセロニアシミュレーター作ってみる

参考にしたサイトはこちら
→https://meganeprog.com/flutter-reversi/

かなり雑な記事です。見る際は注意！！
更新とともに充実させていくつもりではあります。

まずはGameWidget.dartの理解から。
GPT先生の力を全面的に借りてます。

## ひとまずの目標
- アニメーションはなくす。シミュレーターには邪魔でしかない。
- オセロニアのバトルを実際に再現しながら、ダメージ量を確かめられるようなものを目指す。

- ### ゲームのUI
  ```dart
  class GameWidget extends StatefulWidget {
  const GameWidget(this._gameLogic, {super.key});
  
  @override
  State<GameWidget> createState() => _GameWidgetState(_gameLogic);
  
  final GameLogicInterface _gameLogic;
  }) 
  ```
- [GPT先生の見解1-8](https://sl.bing.net/j779EaQ3zc4)
- [GPT先生の見解1-2](https://sl.bing.net/od42IQ87LE)
- [GPT先生の見解4-5](https://sl.bing.net/ep3RvWi1hAa)
- [GPT先生の見解7](https://sl.bing.net/kJq7TpQJ4lo)
- ### ゲームUIのステート
  ```dart
  class _GameWidgetState extends State<GameWidget> with TickerProviderStateMixin {
    _GameWidgetState(this._gameLogic);
  
    @override
    initState() {
      super.initState();
  
      _gameLogic.init();
  ```
- [GPT先生の見解1-8](https://sl.bing.net/cOhKfHlRsUC)
- [GPT先生の見解1](https://sl.bing.net/ijKqoI4iHqC)
- [GPT先生の見解1-2](https://sl.bing.net/hvfsHyRIdau)
- [GPT先生の見解5-8](https://sl.bing.net/g3lTxAraBJk)
- ### 石を裏返すためのコントローラーを生成
  ```dart
      _stoneFlipControllers = List.generate(
          _gameLogic.boardRows,
          (int index) => List.generate(
              _gameLogic.boardColumns, (int index) => FlipCardController()));
  ```
- [GPT先生の見解1-4](https://sl.bing.net/jOuWaSo5gfA)
- [GPT先生の見解1-2](https://sl.bing.net/h6imlBsIdFc)
- [GPT先生の見解3-4](https://sl.bing.net/kvgvEkbcrTw)
- [GPT先生の見解4](https://sl.bing.net/dN7YK9lQHxQ)
- ### ターンをあらわすカードのアニメーション
  ```dart
      _turnCardAnimationController = AnimationController(
        duration: const Duration(milliseconds: 1000),
        vsync: this,
      );
  ```
- [GPT先生の見解1-4](https://sl.bing.net/iTsGRe3TI6K)
- [GPT先生の見解一部](https://sl.bing.net/6epltj6z92)
- ### ターンをあらわすカードのアニメーション（続き）
  ```dart
      _turnCardAnimation = TweenSequence<double>([
        TweenSequenceItem<double>(
          tween:
              Tween(begin: 0.0, end: 0.5).chain(CurveTween(curve: Curves.ease)),
          weight: 15,
        ),
        TweenSequenceItem<double>(
          tween: ConstantTween<double>(0.5),
          weight: 70,
        ),
        TweenSequenceItem<double>(
          tween:
              Tween(begin: 0.5, end: 1.0).chain(CurveTween(curve: Curves.ease)),
          weight: 15,
        ),
  ```
- [GPT先生の見解1-15](https://sl.bing.net/bHojcY0WMyy)
- [GPT先生の見解1-6](https://sl.bing.net/fFUIa9AfaaO)
- [GPT先生の見解7-10](https://sl.bing.net/bQ2rQlMxx3k)
- [GPT先生の見解11-15](https://sl.bing.net/f9io3zPXBuu)
- ### ターンをあらわすカードのアニメーション（続き）
  ```dart
      ]).animate(_turnCardAnimationController)
        ..addListener(() {
          setState(() {});
        })
  ```
- [GPT先生の見解1-4](https://sl.bing.net/gQk7WJzxjCC)
- ### ターンをあらわすカードのアニメーション（続き）
  ```dart
        ..addStatusListener((status) {
          if (status == AnimationStatus.completed) {
            _animationPlaying = false;
            _gameLogic.hint();
          }
        });
  ```
- [GPT先生の見解1-6](https://sl.bing.net/wLcPTy6rk)
- ### 終了アニメーション
  ```dart
      _finishCardAnimationController = AnimationController(
        duration: const Duration(milliseconds: 500),
        vsync: this,
      );
  ```
- [GPT先生の見解1-4](https://sl.bing.net/f6y6A9wUUwK)
- ### 終了アニメーション（続き）
  ```dart
      _finishCardAnimation = TweenSequence<double>([
        TweenSequenceItem<double>(
          tween:
              Tween(begin: 0.0, end: 0.5).chain(CurveTween(curve: Curves.easeIn)),
          weight: 60,
        ),
        TweenSequenceItem<double>(
          tween: Tween(begin: 0.5, end: 0.4)
              .chain(CurveTween(curve: Curves.easeOut)),
          weight: 20,
        ),
        TweenSequenceItem<double>(
          tween:
              Tween(begin: 0.4, end: 0.5).chain(CurveTween(curve: Curves.easeIn)),
          weight: 20,
        ),
  ```
- [GPT先生の見解1-15](https://sl.bing.net/cbVDQ9duRMW)
- [GPT先生の見解1-6](https://sl.bing.net/cbVDQ9duRMW)
- [GPT先生の見解7-11](https://sl.bing.net/gHMAm1j5F3k)
- [GPT先生の見解12-15](https://sl.bing.net/bkQT6CVo2i4)
- どこかで見たような構図なので省略しようかとも思いましたが、簡単に出力できたため多めの出力です。
- ### 終了アニメーション（続き）
  ```dart
      ]).animate(_finishCardAnimationController)
        ..addListener(() {
          setState(() {});
        });
  ```
- [GPT先生の見解1-4](https://sl.bing.net/buKPessdFjo)
- これもどこかで見たような構図…
- ### 紙吹雪のコントローラーの初期化
  ```dart
      _confettiController = ConfettiController(
        duration: const Duration(milliseconds: 500),
      );
  ```
- [GPT先生の見解1-3](https://sl.bing.net/BzeOawBYtw)
- ### 最初のターンの通知
  ```dart
      _animationPlaying = true;
      Future.delayed(const Duration(seconds: 1), () {
        _turnCardAnimationController.forward();
      });
    }
  ```
- [1-5](https://sl.bing.net/gxxnbBfoMaO)
- めんどくさくなったので「GPT先生の見解」省略！！
- ### 最初のターンの通知（続き）
  ```dart
    @override
    void dispose() {
      _turnCardAnimationController.dispose();
      _finishCardAnimationController.dispose();
      _confettiController.dispose();
      super.dispose();
    }
  ```
- [1-7](https://sl.bing.net/iqr4vKVLPOK)
- ### 最初のターンの通知（続き）
  ```dart
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Reversi'),
        ),
        body: Stack(
          children: <Widget>[
            Center(
              child: _buildBoard(context),
            ),
            _buildTurnCard(context),
            _buildFinishCard(context),
            _buildConfetti(context),
          ],
        ),
      );
    }
  ```
- [1-18](https://sl.bing.net/dbBXMWUJHxI)
- [1-4](https://sl.bing.net/gD5PJFYKUXk)
- [5-8](https://sl.bing.net/dAwUfTjwcge)
- [9-14](https://sl.bing.net/jYRvVsfpJV6)
- ### 最初のターンの通知（続き）
  ```dart
    Future<void> _putStone(int row, int column) async {
      if (_animationPlaying) {
        return;
      }
      List<Position> reversePositions =
          _gameLogic.getReversePositions(row, column, _gameLogic.turn);
      if (reversePositions.isNotEmpty) {
        _animationPlaying = true;
        setState(() {
          _gameLogic.clearHint();
          _gameLogic.putStone(row, column, _gameLogic.turn);
        });
        await Future.delayed(const Duration(milliseconds: 200));
        for (Position position in reversePositions) {
          _stoneFlipControllers[position.row][position.column].toggleCard();
          await Future.delayed(const Duration(milliseconds: 250));
          setState(() {
            _gameLogic.reverseStone(position.row, position.column);
            _stoneFlipControllers[position.row][position.column]
                .toggleCardWithoutAnimation();
          });
        }
        await Future.delayed(const Duration(milliseconds: 200));
        setState(() {
          if (_gameLogic.changeTurn()) {
            _turnCardAnimationController.reset();
            _turnCardAnimationController.forward();
          } else {
            // ゲームオーバー
            _finishCardAnimationController.forward();
            if (_gameLogic.numberOfStone(Stone.white) !=
                _gameLogic.numberOfStone(Stone.black)) {
              _confettiController.play();
            }
          }
        });
      }
    }
  
  ```
- [1-39](https://sl.bing.net/gAapAfN3kxU)