FBDでパズルを作成します。 フィフティーンとシンプソン

こんにちは。



この記事では、 FBDプログラミング言語で簡単なプログラムを作成する方法を示しますが、これは有用なことを行います。 この例では、これは15のゲームになります。



最初に、 ゲームのルールを思い出します。「15」、「15」、「Taken」のゲームは、1878年にノアチャップマンによって発明された人気のパズルです。 これは、正方形のボックスに数字が印刷された同一の四角ナックルのセットです。 ボックスの辺の長さは、15個の要素のセットのナックルの辺の長さの4倍であるため、1つの四角形のボックスは空のままです。 ゲームの目的は、ナックルをボックスの周りで動かし、数字で並べることです。できればできるだけ少ない動きにすることです。



ご覧のとおり、ルールは非常に単純です。 実装は簡単で、 グラフィック部分なしで約15分かかり、すべての写真で30分かかります。 同時に、アルゴリズムとロジックの最適化の問題はこの記事の範囲を超えているという事実に注意を喚起したいと思います。 これらの質問はそれほど単純ではなく、さらに時間がかかります。



結果は次のとおりです。





プログラムの説明、コメント、カットの下の写真。



プログラムの基礎



ゲームの説明からわかるように、16個の要素のフィールドがあります。 したがって、プログラムの基本は「セルケージ」という要素になります。

この要素の要件を策定します。



-プレーヤーが隣の空いている場所に移動したいと言った場合、「タグセル」は外部チームを受け入れなければなりません。

-セルは、隣に空のセルがあり、その隣に場所を入れ替えることができるかどうかを理解するために、垂直方向および水平方向に隣接セルについて知る必要があります。

-ケージはゲームの開始時に初期値を設定する必要があります。

-セルは、現在設定されている値を出力に送信する必要があります。

-プレイヤーがケージに隣接する空のフィールドに移動するよう指示した場合、ケージは隣の空のケージにコマンドを送信して、場所を変更する必要があります。



これらの要件に基づいて、入力信号と出力信号のセットを取得します。



インプット



出力:



15個のセル自体はどこにも実行されず、単に意味を上書きするため、引用符で「場所を入れ替える」。



結果はこのマクロです:





このようなマクロをタスクに16個入れて、それらを接続します。 その結果、以下が得られます。



すべてが非常に簡単です。 16個のマクロ「15個のセル」をタスクに設定し、各セルを縦横に隣接するセルに接続しました。 セルに隣人がいない場合(たとえば、cell_1_1には左上に隣人がいない場合)、対応する入力に「-1」を入力します。



コアスタッフィング



最後のステップで、プログラムの基礎を築きました。 しかし、以来 「15セル」要素自体はまだ空であるため、プログラムは良いことをする方法を知りません。 タグセルのロジックを入力して、これを修正します。







これが、マクロの完成した実装です。 簡単に言えば、仕事の原則:



1.入力で制御コマンドを受け取り、展開します。 コマンドが到着したことを示すサインとコマンドパラメーターという、1サイクルの期間を持つ信号を個別に選び出します。 私たちの場合、コマンドパラメーターは1から6の整数のセットで、数字の1から4は近隣の1つと値を交換するコマンド、数字の5はセルに初期値を設定するコマンド、数字の6はプレイヤーがこのセルをクリックしたことを意味し、彼女を隣の空のフィールドに「移動」したい。



2.すべてのネイバーのゼロ値をチェックします(0は空のフィールドです)。



3.次に、このセルの現在の値とコマンドを確認します。 セルの値がゼロ(セルが空)であり、同時にコマンドが近隣と値を交換するようになった場合(コマンド1〜4)、セル内の近隣のセルの値を上書きします。



4.コマンド5(初期値を設定)を受け取った場合、単に「SetValue」入力から値をメモリに書き込みます。



5.コマンド6(プレーヤーが特定のセルをクリックした)を受信し、隣接セルの1つの値がゼロの場合、このセルにゼロ値を書き込み、交換する隣接セルを示す「Let's Go」コマンドを隣接セルに送信します。



それだけです。 実装の唯一の微妙な点は、「遅延」アルゴリズムです。 しかし、それらが必要な理由を推測するために、プログラムがどのように実行されるかを簡単に想像することができます。



-このセルを空の場所に「移動」するコマンドをプレイヤーから受け取りました。

-チェック、本当に空の場所が近くにあります。

-セル「0」に記録され、隣人に値を交換したいことを伝えました。

-隣人(同じマクロ「15セル」を満たしていることを理解する必要がありますが、その別のインスタンス)は、場所を別のセルと交換するコマンドを受け取ったことを確認します。

-ネイバーは、「0」が書き込まれていることを確認します。 彼は今は空のセルであり、自分が最初のセルの意味を書き、そこからコマンドが場所を切り替えるようになりました。



ちょっと待って! 実際、コマンドを隣人に送信する方法と同時に、最初のセルに「0」を書き込みました。したがって、隣人もゼロを書き込み、2つの空のセルを取得し、1つの値(行1〜15)が消えます。 そして、競技場全体が空になるまで。 これを防ぐために、最初のセルの上書きをゼロで遅らせます。 したがって、空の隣接セルには、正しい値を記録する時間があります。



最適化に関するいくつかの言葉
ご覧のとおり、「15セル」マクロ自体は非常に単純です。 ただし、そこからいくつかのブロックを捨てることにより、さらに単純化することができます。 アイデアはシンプルです。 順列を実行する前に、チームと隣接セルを確認します。 ただし、上記のように、順列を使用して、同じマクロの2つのインスタンスを処理します。 そして、最初のインスタンスではコマンドを送信する前にチェックし、2番目のインスタンスではコマンドを受信したときに同じチェックを行います。 したがって、チェックの1つを安全にスローできます。 これはすぐに目を引く最初のものです。 2番目のポイントは、値の余分な遅延です。 結局、値は「メモリ」ブロックに保存され、コマンドによってのみ上書きされます。 したがって、コマンドのみを遅らせて値を上書きし、値自体をそのまま送信するだけで十分です。


勝利または敗北



実際、私たちのプログラムの基礎は整っており、すでにうまく機能しています。 プレイできます。 追加のアクションはすべて、補助機能の実装にのみ必要です。



プレイヤーが勝つか負けるかを自動的にチェックすることから始めましょう。



「15」というゲームでは、数字「14」と「15」のタイルが正しい順序でない場合、すべての可能な初期タイルレイアウトの可能なレイアウトの半分を組み立てることができないという面白い瞬間があります。



これに基づいて、2つの条件を形成します。

-条件1:各セルに記録された値は、そのシリアル番号に対応しています。 つまり プレーヤーが勝った。

-条件2:セルに記録された値は、交換されるセル「14」と「15」を除き、シリアル番号に対応します。 つまり レイアウトには少し不運です。



18個の比較アルゴリズムを配置し、Iによってそれぞれ16個の等式の2つのセットを収集します。 ORにより、線形の両端の1つに到達したという兆候を形成します。



18個のアルゴリズム。最初のケースでは16個のセルの対応をチェックし、2番目のケースではさらにセル「14」と「15」の順列をチェックするためです。 ところで、なぜなら 事前に定義されたデータ配列があるので、フィールド全体の15回のチェックで十分です。 最後のセルが自動的にチェックされます。







初期配置



これで、プログラムの準備ができました。 プレイでき、勝った場合はメッセージが表示されます。 ゲームの開始時にフィールドのタイルをランダムに配置する必要があることを除いて、すべてが問題ありません。 そして、この例ではこれが最も難しいタスクであることがわかりました。 ただし、速度、アルゴリズムの最適性などに直面しない場合 その後、小さな血液でこの問題を比較的迅速に「正面から」解決できます。



最初に、ランダムな整数ジェネレーター0〜15を作成します。古い実証済みの方法で行こう。 これは完全に受け入れられるオプションです。 結果の乱数のシーケンスは、「New Game」ボタンが押された時点に依存します。 そして以来 この瞬間が起こり、繰り返されることはありません。最終的に、非常にシンプルで優れた乱数ジェネレーターが得られます。







Real-in-Integer(DC)アルゴリズムの出力で、0〜15のランダムな整数値を取得します。



ここでのタスクは、一連の非反復値を形成し、各値をそのセルに書き込むことです。



先ほど言ったように、この問題に対してFBD言語専用の多かれ少なかれ優れたソリューションを作ることは困難です。 または、他の人には明らかな美しいシンプルなソリューションが表示されないだけかもしれません。 もちろん、STに挿入するオプションは常にありますが、目標はタスクを完全にFBDで行うことです。



「額」問題の解決策は次のとおりです。



-コントロール要素を配置します。

-コマンドを与えるとき、最初の配列「-1」を打ち込みます(範囲[0..15]以外の任意の数で可能でした)。

-カウンターをリセットします。

-次に、0〜15の乱数を生成し、配列にそのようなものがあるかどうかを確認します。 ある場合は、引き続き乱数を生成します。

-そのような数がない場合、カウンターを1つ増やし、対応するメモリセルに値を書き込みます。

-これらの手順をさらに15回繰り返します。

-16番目のステップに到達したかどうかを確認し、パルスを形成して、これらの値をすべてのセルに記録するコマンドを送信します。



このアルゴリズムの主な欠点は、以前にスローされた値の複数の検索です。 理論的には、このようなランダムシーケンスの生成時間は無限になります。 実際には、数十のプログラムが実行され、0.5秒を超えませんでした。 いずれの場合でも、生成時に(瞬間的ではないため)、プレーヤーのアクションにロックをかけます。



結果は次のとおりです。







グラフィックを添付



タスクが完全に準備できたので、UIを添付します。



これを行うには、インターネット上でキャッチフレーズ付きの写真を選択します。 同時に、位置合わせの成功したコレクションと失敗した写真を選択します。 ホーマーシンプソンをそこに置くことにしました。



16枚の画像を配置し、アニメーションを添付して、セル内の数字に対応する数字を表示します。 そして、ゼロのセルでは、タイルは非表示でなければなりません。 各タイルに「6」チームの編成を配置しました。 以下に、「New Game」ボタンがあります。これにより、ランダムな初期配置を生成するアルゴリズムを実行します。 同時に、生成中にすべてのタイルにロックをかけることを忘れないでください。 このすべての上に、ホーマーを置きます。 それだけです グラフィックの準備ができたら、プレイできます。



描画オプション。 最初に、ユニットにあるすべてのタイルに気付くかもしれません。





開始位置。





アライメントに失敗しました。





そして勝利!





未実現のままであるもの



実際にはそれほどではありません:



-あなたは動きのカウンターを取り付けることができます。 これは基本的に行われます。 タグのタグセルには、6人のチーム(プレイヤーのチーム)があります。 1サイクル続く論理信号を取得すれば十分です。 この信号を引き出します。 ORで16個すべての信号を収集し、フィードバックでカバーされる加算アルゴリズムに追加します。 つまり 追加のアルゴリズムは3つだけで、数分で済みます。



-統計。 勝ったゲームと失敗したゲームのカウンターを作成できます。 2つの追加アルゴリズムとバインディングアルゴリズムのペアが追加されます。 また、数分の作業。



-手の選択。 既に述べたように、タイル「14」と「15」を交換できないため、レイアウトの半分は最初は収束しません。 「悪い取引」の開始時に確認して、これらの2つのタイルをすぐに変更できます。



これをすべて行うことは難しくないので、私はこの機会を皆に任せます。



結論



今回はFBD言語で30分で、単純なおもちゃ「フィフティーン」が実現しました。 さらに、使用されるすべてのアルゴリズムはシンプルで明確です。 信号処理のロジック全体は明らかです。 このようなプログラムを理解することは、ロジックの基礎とプログラミング言語IEC 61131の基礎に精通している人にとって難しくありません。



チェックのために、私はCについてもほぼ同じことをしました。

Cのプログラムのテキスト
なぜなら 私はC / C ++でのプログラミングを知らないので、おそらくプログラムはひどいです。 さらに、私はすぐにいくつかの「疑わしい」場所を見ます。 ただし、それは機能し、エラーなしで機能します。



主な考え方は、C言語とFBD言語で記述された同じプログラムは、「外部から」の人による理解の複雑さが異なるということです。 機能ブロックの言語を理解することが難しくない場合は、Cの実装をいじる必要があります。



また、FBDプログラムは153ブロックで構成されますが(16個のマクロがあり、それぞれが32ブロックで構成されていることを覚えています)、Cで50行のコードを記述するよりもはるかに短い時間で記述できます。



#include <iostream> #include "cstdlib" #include <locale.h> #include <stdio.h> #include <conio.h> #include <math.h> #include <memory.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <cmath> using namespace std; int main(void) { int MyPyatn[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; int i = 0, x = 0, y = 0, MyRand = 0, MyBuffer = 0, MyButton = 0; bool MyGameover = false, MyChange = false; setlocale(LC_ALL, "ru-RU"); srand(time(NULL)); cout << " 'n'         : "; kbhit(); if(getch() != 110) return 0; std::system("cls"); cout << "  : "; for(i=0;i<16;i++) { MyRand = rand() %(16-i); MyBuffer = MyPyatn[15-i]; MyPyatn[15-i] = MyPyatn[MyRand]; MyPyatn[MyRand] = MyBuffer; } for (i=0;i<16;i++) { if ((i %4) == 0) { cout << "\n"; cout << "\n"; } if ((MyPyatn[i]<10) || (MyPyatn[i] == 16)) cout << " "; else cout << " "; if (MyPyatn[i] == 16) { cout << "_"; x = i %4 + 1; y = int (i/4) + 1; } else cout << MyPyatn[i]; cout << " "; } cout << "\n"; cout << "\n"; cout << "  ---.  - '0' \n"; cout << " _   "; do { MyChange = false; MyGameover = true; for(i=0;i<16;i++) { if (MyPyatn[i] != (i+1)) MyGameover = false; } while(!kbhit ()); MyButton = getch(); if ((MyButton == 48) || (MyButton == 72) || (MyButton == 75) || (MyButton == 77) || (MyButton == 80)) { if (MyButton == 48) return 0; if ((MyButton == 72) && (y > 1)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-2)*4 + x - 1]; MyPyatn[(y-2)*4 + x - 1] = MyBuffer; y = y - 1; MyChange = true; } if ((MyButton == 80) && (y < 4)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[y*4 + x - 1]; MyPyatn[y*4 + x - 1] = MyBuffer; y = y + 1; MyChange = true; } if ((MyButton == 75) && (x > 1)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-1)*4 + x - 2]; MyPyatn[(y-1)*4 + x - 2] = MyBuffer; x = x - 1; MyChange = true; } if ((MyButton == 77) && (x < 4)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-1)*4 + x]; MyPyatn[(y-1)*4 + x] = MyBuffer; x = x + 1; MyChange = true; } } if (MyChange) { std::system("cls"); cout << "  : "; for (i=0;i<16;i++) { if ((i %4) == 0) { cout << "\n"; cout << "\n"; } if ((MyPyatn[i]<10) || (MyPyatn[i] == 16)) cout << " "; else cout << " "; if (MyPyatn[i] == 16) cout << "_"; else cout << MyPyatn[i]; cout << " "; } cout << "\n"; cout << "\n"; cout << "  ---.  - '0' \n"; cout << " _   "; } } while(!MyGameover); std::system("cls"); cout << "!!!"; getch(); return 0; }
      
      







言い換えれば、FBD言語は、プログラミングから遠く離れた人々のために設計されたシンプルで理解しやすい言語です。 簡単なタスクのプログラミングは簡単です。 また、他の人が書いたプログラムの原則を理解することも、通常は大きな問題にはなりません。



以上です。 面白かったと思います。



All Articles