ルービックキューブ描画アルゴリズム(完成版)とFlutterでアプリ開発する課題点について

# はじめに
以前書いた記事でルービックキューブの与えられたScrambleに対して，そのvisualを表示するアルゴリズムを提唱しました．当時は実装段階だったのですがそのあと3日くらいで完成はしていました．完成したアルゴリズムと，その後のアプリ開発で出てきた課題を残しておきたいと思います．

# 概要
できたアルゴリズムは以前あげた記事で解説済みなので，詳しく知りたい方はそちらを参考にしていただければと思います．

https://qiita.com/hyuga20011212/items/8e3446993084ee44573e

ここでは簡単に説明を加えておきます．
3×3のブロックが6面分あるので，それらを配列で保持します．
ルービックキューブには回転記号が定められているので，それに対して1つひとつ配列の処理を定義していったという形になります．Scrambleは「D2 L' F2 D2 U L2 F2 L2 U' B2 U B2 F2 U' L F2 L D' L' B' R'」のようになっているので，先ほど定義した処理を頭から順番に1個ずつ実行していけば大丈夫です．

# 実行例
実行結果を以下に示します．
画像左が既存のタイマーアプリです．
上に「F2～～U2」と書いてあるのがScrambleで，その混ぜ方で混ぜると右下のような展開図になるという形です．
実際に作ったアルゴリズムにスクランブルを入力してみました．すると数字で表された展開図が表示されます．
白1:オレンジ2：黒(緑)3：赤4：青5：黄6に対応しているので既存タイマーの展開図と，今回作ったアルゴリズムで生成した展開図が一致していることが確認できます．
あとはこの出力された数字を色と対応できるように加工すればいいのかなーと思っていました．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3888054/8898e25f-cec3-a1e7-f162-a4b1c9d31c0a.png)

# Flutterアプリを作ろうとして～
iphoneで使えるアプリを1個作ろうと思いFlutterで開発にいたりました．
欲しい機能としては
・いわゆるタイマー機能(できればボタンを押してスタートではなく，画面タップでスタートしたい)
・Scrambleの自動生成
・Scramble後の展開図の描画
・測定したタイムのCRUD機能
・測定したタイムの詳細画面
・直近5回，12回，100回の平均やソルブ回数を出力する機能
基本的にはtodoアプリの拡張で完成できると思っていて，CRUD機能+αみたいなアプリになるのかなあと感じていました．RUDはわりとそのままに，createだけタイマーで入力されるような形？

## ぶつかった障害について
画像2枚が開発段階のものです．わりと形にはなってきていて，あとは細かい機能や画面遷移を実装するだけのはずなんですが．最大の壁は展開図が上手く出力できんってことです．

Flutterを少しでも触ったことがある人は分かると思いますが，この言語はわりと特殊で，表示される画面をwidgetで組み立てていきます．HTML，CSSで画面を作るのとは勝手が違うんですよね．
なのでHTML，CSS感覚で展開図作ろうとしたら簡単なのに，Flutterで作ろうとしたらわけわからんことになります．

わりと色々調べましたが，Flutterで展開図を作るのはどうやら向いてなさそう．
研究室では頻繁に勉強回が開かれるのでFlutterに詳しい先輩に展開図をどうすればいいか聞いてみました．
どうやら四角のオブジェクトを3×3(1面)×6面分=54個配置して作ればいいのでは？と言われちゃいました．作れなくはないけど，かなりめんどくさそう．しかも1回解くごとに裏でアルゴリズム回して，出た結果を54個のオブジェクト受け渡していくのか...
技術的には可能だけどっていうところですね
重い腰を上げれないまま早1カ月，はたしていつ完成するのかというところです．
あとはサンプルコード載せて終わりにします．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3888054/7e88e48f-3898-32bd-3927-bebbb50d7178.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3888054/9b63b7df-eb1b-0d38-2cfc-43232115d8db.png)

# ルービックキューブ描画アルゴリズムのサンプルコード
```C++:cubevisual.cpp
#include <iostream>
#include <sstream>
#include <vector>

void printArrayRow(int array[3][3], int row) {
    // 指定された行の内容を表示
    for (int j = 0; j < 3; j++) {
        std::cout << array[row][j] << " ";
    }
}

// 1から6までの数字で埋めた6つの配列を定義
int array1[3][3] = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} };//白
int array2[3][3] = { {2, 2, 2}, {2, 2, 2}, {2, 2, 2} };//オレンジ
int array3[3][3] = { {3, 3, 3}, {3, 3, 3}, {3, 3, 3} };//緑
int array4[3][3] = { {4, 4, 4}, {4, 4, 4}, {4, 4, 4} };//赤
int array5[3][3] = { {5, 5, 5}, {5, 5, 5}, {5, 5, 5} };//青
int array6[3][3] = { {6, 6, 6}, {6, 6, 6}, {6, 6, 6} };//黄色

int arraycopy1[3][3] = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} };
int arraycopy2[3][3] = { {2, 2, 2}, {2, 2, 2}, {2, 2, 2} };
int arraycopy3[3][3] = { {3, 3, 3}, {3, 3, 3}, {3, 3, 3} };
int arraycopy4[3][3] = { {4, 4, 4}, {4, 4, 4}, {4, 4, 4} };
int arraycopy5[3][3] = { {5, 5, 5}, {5, 5, 5}, {5, 5, 5} };
int arraycopy6[3][3] = { {6, 6, 6}, {6, 6, 6}, {6, 6, 6} };

// 各回転操作の関数を定義
void turnU() {//Uの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }
    //F→U
    array2[0][0] = arraycopy3[0][0];
    array2[0][1] = arraycopy3[0][1];
    array2[0][2] = arraycopy3[0][2];
    //D→F
    array3[0][0] = arraycopy4[0][0];
    array3[0][1] = arraycopy4[0][1];
    array3[0][2] = arraycopy4[0][2];
    //B→D
    array4[0][0] = arraycopy5[0][0];
    array4[0][1] = arraycopy5[0][1];
    array4[0][2] = arraycopy5[0][2];
    //U→B
    array5[0][0] = arraycopy2[0][0];
    array5[0][1] = arraycopy2[0][1];
    array5[0][2] = arraycopy2[0][2];
    //U→U
    array1[0][0] = arraycopy1[2][0];
    array1[1][0] = arraycopy1[2][1];
    array1[2][0] = arraycopy1[2][2];
    array1[0][1] = arraycopy1[1][0];
    array1[2][1] = arraycopy1[1][2];
    array1[0][2] = arraycopy1[0][0];
    array1[1][2] = arraycopy1[0][1];
    array1[2][2] = arraycopy1[0][2];
}


void turnR() {//Rの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }
    
    array1[0][2] = arraycopy3[0][2];
    array1[1][2] = arraycopy3[1][2];
    array1[2][2] = arraycopy3[2][2];
    
    array3[0][2] = arraycopy6[0][2];
    array3[1][2] = arraycopy6[1][2];
    array3[2][2] = arraycopy6[2][2];
    
    array6[0][2] = arraycopy5[2][0];
    array6[1][2] = arraycopy5[1][0];
    array6[2][2] = arraycopy5[0][0];
    
    array5[2][0] = arraycopy1[0][2];
    array5[1][0] = arraycopy1[1][2];
    array5[0][0] = arraycopy1[2][2];
    
    array4[0][0] = arraycopy4[2][0];
    array4[1][0] = arraycopy4[2][1];
    array4[2][0] = arraycopy4[2][2];
    array4[0][1] = arraycopy4[1][0];
    array4[2][1] = arraycopy4[1][2];
    array4[0][2] = arraycopy4[0][0];
    array4[1][2] = arraycopy4[0][1];
    array4[2][2] = arraycopy4[0][2];
}

void turnL() {//Lの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }

    array1[0][0] = arraycopy5[2][2];
    array1[1][0] = arraycopy5[1][2];
    array1[2][0] = arraycopy5[0][2];

    array5[0][2] = arraycopy6[2][0];
    array5[1][2] = arraycopy6[1][0];
    array5[2][2] = arraycopy6[0][0];

    array6[2][0] = arraycopy3[2][0];
    array6[1][0] = arraycopy3[1][0];
    array6[0][0] = arraycopy3[0][0];

    array3[0][0] = arraycopy1[0][0];
    array3[1][0] = arraycopy1[1][0];
    array3[2][0] = arraycopy1[2][0];

    array2[0][0] = arraycopy2[2][0];
    array2[1][0] = arraycopy2[2][1];
    array2[2][0] = arraycopy2[2][2];
    array2[0][1] = arraycopy2[1][0];
    array2[2][1] = arraycopy2[1][2];
    array2[0][2] = arraycopy2[0][0];
    array2[1][2] = arraycopy2[0][1];
    array2[2][2] = arraycopy2[0][2];
}

void turnD() {//Dの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }

    array3[2][0] = arraycopy2[2][0];
    array3[2][1] = arraycopy2[2][1];
    array3[2][2] = arraycopy2[2][2];

    array2[2][0] = arraycopy5[2][0];
    array2[2][1] = arraycopy5[2][1];
    array2[2][2] = arraycopy5[2][2];

    array5[2][0] = arraycopy4[2][0];
    array5[2][1] = arraycopy4[2][1];
    array5[2][2] = arraycopy4[2][2];

    array4[2][0] = arraycopy3[2][0];
    array4[2][1] = arraycopy3[2][1];
    array4[2][2] = arraycopy3[2][2];

    array6[0][0] = arraycopy6[2][0];
    array6[1][0] = arraycopy6[2][1];
    array6[2][0] = arraycopy6[2][2];
    array6[0][1] = arraycopy6[1][0];
    array6[2][1] = arraycopy6[1][2];
    array6[0][2] = arraycopy6[0][0];
    array6[1][2] = arraycopy6[0][1];
    array6[2][2] = arraycopy6[0][2];
}

void turnB() {//Bの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }

    array1[0][2] = arraycopy4[2][2];
    array1[0][1] = arraycopy4[1][2];
    array1[0][0] = arraycopy4[0][2];

    array4[0][2] = arraycopy6[2][2];
    array4[1][2] = arraycopy6[2][1];
    array4[2][2] = arraycopy6[2][0];

    array6[2][2] = arraycopy2[2][0];
    array6[2][1] = arraycopy2[1][0];
    array6[2][0] = arraycopy2[0][0];

    array2[0][0] = arraycopy1[0][2];
    array2[1][0] = arraycopy1[0][1];
    array2[2][0] = arraycopy1[0][0];

    array5[0][0] = arraycopy5[2][0];
    array5[1][0] = arraycopy5[2][1];
    array5[2][0] = arraycopy5[2][2];
    array5[0][1] = arraycopy5[1][0];
    array5[2][1] = arraycopy5[1][2];
    array5[0][2] = arraycopy5[0][0];
    array5[1][2] = arraycopy5[0][1];
    array5[2][2] = arraycopy5[0][2];
}

void turnF() {//Fの回転を定義
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            arraycopy1[i][j] = array1[i][j];
            arraycopy2[i][j] = array2[i][j];
            arraycopy3[i][j] = array3[i][j];
            arraycopy4[i][j] = array4[i][j];
            arraycopy5[i][j] = array5[i][j];
            arraycopy6[i][j] = array6[i][j];
        }
    }
    
    array1[2][0] = arraycopy2[2][2];
    array1[2][1] = arraycopy2[1][2];
    array1[2][2] = arraycopy2[0][2];
    
    array2[0][2] = arraycopy6[0][0];
    array2[1][2] = arraycopy6[0][1];
    array2[2][2] = arraycopy6[0][2];
    
    array6[0][0] = arraycopy4[2][0];
    array6[0][1] = arraycopy4[1][0];
    array6[0][2] = arraycopy4[0][0];
    
    array4[0][0] = arraycopy1[2][0];
    array4[1][0] = arraycopy1[2][1];
    array4[2][0] = arraycopy1[2][2];
    
    array3[0][0] = arraycopy3[2][0];
    array3[1][0] = arraycopy3[2][1];
    array3[2][0] = arraycopy3[2][2];
    array3[0][1] = arraycopy3[1][0];
    array3[2][1] = arraycopy3[1][2];
    array3[0][2] = arraycopy3[0][0];
    array3[1][2] = arraycopy3[0][1];
    array3[2][2] = arraycopy3[0][2];
}

//入力された回転記号と定義した関数を関連付ける
void executeTurn(const std::string& turn) {
    if (turn == "U") { turnU(); }
    else if (turn == "U2") { turnU(); turnU(); }
    else if (turn == "U'") { turnU(); turnU(); turnU(); }//回転記号Uについて

    else if (turn == "R") { turnR(); }
    else if (turn == "R2") { turnR(); turnR(); }
    else if (turn == "R'") { turnR(); turnR(); turnR(); }//回転記号Rについて

    else if (turn == "L") { turnL(); }
    else if (turn == "L2") { turnL(); turnL(); }
    else if (turn == "L'") { turnL(); turnL(); turnL(); }//回転記号Lについて

    else if (turn == "D") { turnD(); }
    else if (turn == "D2") { turnD(); turnD(); }
    else if (turn == "D'") { turnD(); turnD(); turnD(); }//回転記号Dについて

    else if (turn == "F") { turnF(); }
    else if (turn == "F2") { turnF(); turnF(); }
    else if (turn == "F'") { turnF(); turnF(); turnF(); }//回転記号Fについて

    else if (turn == "B") { turnB(); }
    else if (turn == "B2") { turnB(); turnB(); }
    else if (turn == "B'") { turnB(); turnB(); turnB(); }//回転記号Dについて


    else std::cout << "Unknown move: " << turn << std::endl;
}

int main() {
    // 手入力で文字列を取得
    std::string input;
    std::cout << "Please enter the scramble: ";
    std::getline(std::cin, input);

    // 取得した文字列を処理
    std::istringstream iss(input);
    std::string turn;

    // 入力をスペースで分割して各ターンの関数を実行
    while (iss >> turn) {
        executeTurn(turn);
    }

    //////////////////////////////////////////////////////////////////////////
    //スクランブル後を描画
    // 配列1（上部）を表示
    std::cout << "        "; // 左のスペースを追加
    for (int i = 0; i < 3; i++) {
        printArrayRow(array1, i);
        std::cout << std::endl;
        if (i != 2) std::cout << "        "; // 次の行の左のスペースを揃える
    }
    std::cout << "\n";

    // 配列2, 3, 4, 5（中央）を表示
    for (int i = 0; i < 3; i++) {
        printArrayRow(array2, i);
        std::cout << "  "; // 配列間のスペース
        printArrayRow(array3, i);
        std::cout << "  "; // 配列間のスペース
        printArrayRow(array4, i);
        std::cout << "  "; // 配列間のスペース
        printArrayRow(array5, i);
        std::cout << std::endl;
    }
    std::cout << "\n";

    // 配列6（下部）を表示
    std::cout << "        "; // 左のスペースを追加
    for (int i = 0; i < 3; i++) {
        printArrayRow(array6, i);
        std::cout << std::endl;
        if (i != 2) std::cout << "        "; // 次の行の左のスペースを揃える
    }
    //////////////////////////////////////////////////////////////////////////

    return 0;
}

```