/// <summary> /// 再帰してミノの配置を全列挙 /// 負荷削減のために、一度右移動した方向のみにしか移動は不可、回転はあらかじめやっておく /// </summary> /// <param name="mino">現在のテトリミノ</param> /// <param name="field">現在の盤面</param> /// <param name="move">今までの移動履歴を収納(ホールドやネクストを含む)</param> /// <param name="hashset">重複検知用のDictionary、hashsetではない</param> /// <param name="movecount">今までの移動カウント、回転も含む、ホールドも含むはず【要】</param> /// <param name="beforeHorizontal">前の移動と反対側に移動できないようにするための前の横移動履歴収納</param> /// <param name="beforeEval">前のミノ設置の評価、一部継続させないと一番最後のスコアだけが反映されてしまう</param> static private void SearchPattern( TetrisEnvironment.Mino mino, int[,] field, string move, Dictionary <string, TetrisEnvironment.Way> hashset, int movecount, TetrisEnvironment.Move beforeHorizontal, float beforeEval ) { TetrisEnvironment.Mino mino_clone; //ハードドロップ { mino_clone = TetrisEnvironment.Mino.Clone(mino); int count = 0; while (true) { if (TetrisEnvironment.CheckValidPosition(field, mino_clone.positions, new TetrisEnvironment.Vector2(0, count))) { count--; } else { count++; break; } } mino_clone.positions[0] += new TetrisEnvironment.Vector2(0, count); mino_clone.positions[1] += new TetrisEnvironment.Vector2(0, count); mino_clone.positions[2] += new TetrisEnvironment.Vector2(0, count); mino_clone.positions[3] += new TetrisEnvironment.Vector2(0, count); //ハッシュのため順番を整列 for (int i = 1; i < 4; i++) { if (mino_clone.positions[i].x < mino_clone.positions[i - 1].x) { TetrisEnvironment.Vector2 temp = mino_clone.positions[i - 1]; mino_clone.positions[i - 1] = mino_clone.positions[i]; mino_clone.positions[i] = temp; } } for (int j = 1; j < 4; j++) { int num = 0; if (mino_clone.positions[j].x == mino_clone.positions[j - 1].x) { while (j - 1 - num >= 0 && mino_clone.positions[j - num].y < mino_clone.positions[j - 1 - num].y) { TetrisEnvironment.Vector2 temp = mino_clone.positions[j - 1 - num]; mino_clone.positions[j - 1 - num] = mino_clone.positions[j - num]; mino_clone.positions[j - num] = temp; num++; } } } string hash = mino_clone.positions[0].x.ToString(); hash += mino_clone.positions[0].y.ToString(); hash += mino_clone.positions[1].x.ToString(); hash += mino_clone.positions[1].y.ToString(); hash += mino_clone.positions[2].x.ToString(); hash += mino_clone.positions[2].y.ToString(); hash += mino_clone.positions[3].x.ToString(); hash += mino_clone.positions[3].y.ToString(); TetrisEnvironment.Way output; if (hashset.TryGetValue(hash, out output)) { if (output.moveCount > movecount) { hashset.Remove(hash); hashset.Add(hash, new TetrisEnvironment.Way(move + (int)TetrisEnvironment.Move.HardDrop, NewEvaluation.Evaluate(field, mino_clone.positions, beforeEval, false), mino_clone.positions, movecount + 1, hash)); } } else { hashset.Add(hash, new TetrisEnvironment.Way(move + (int)TetrisEnvironment.Move.HardDrop, NewEvaluation.Evaluate(field, mino_clone.positions, beforeEval, false), mino_clone.positions, movecount + 1, hash)); } } //右移動 if (beforeHorizontal != TetrisEnvironment.Move.Left && TetrisEnvironment.CheckValidPosition(field, mino.positions, new TetrisEnvironment.Vector2(1, 0))) { mino_clone = TetrisEnvironment.Mino.Clone(mino); mino_clone.positions[0] += TetrisEnvironment.Vector2.X1; mino_clone.positions[1] += TetrisEnvironment.Vector2.X1; mino_clone.positions[2] += TetrisEnvironment.Vector2.X1; mino_clone.positions[3] += TetrisEnvironment.Vector2.X1; SearchPattern(mino_clone, field, move + (int)TetrisEnvironment.Move.Right, hashset, movecount + 1, TetrisEnvironment.Move.Right, beforeEval); } //左移動 if (beforeHorizontal != TetrisEnvironment.Move.Right && TetrisEnvironment.CheckValidPosition(field, mino.positions, new TetrisEnvironment.Vector2(-1, 0))) { mino_clone = TetrisEnvironment.Mino.Clone(mino); mino_clone.positions[0] += TetrisEnvironment.Vector2.MinusX1; mino_clone.positions[1] += TetrisEnvironment.Vector2.MinusX1; mino_clone.positions[2] += TetrisEnvironment.Vector2.MinusX1; mino_clone.positions[3] += TetrisEnvironment.Vector2.MinusX1; SearchPattern(mino_clone, field, move + (int)TetrisEnvironment.Move.Left, hashset, movecount + 1, TetrisEnvironment.Move.Left, beforeEval); } }
static private void Search(int[,] field, int nowmino, int[] nexts, int ojama, float beforeEval, string moveajust = "") { TetrisEnvironment.Mino mino = TetrisEnvironment.CreateMino((TetrisEnvironment.MinoKind)nowmino); Dictionary <string, TetrisEnvironment.Way> ways = new Dictionary <string, TetrisEnvironment.Way>(); SearchPattern(mino, field, moveajust, ways, 0, TetrisEnvironment.Move.Null, beforeEval); var minoclone = TetrisEnvironment.Mino.Clone(mino); //右回転 if (TetrisEnvironment.RotateMino(TetrisEnvironment.Move.RightRotate, ref mino, field)) { SearchPattern(mino, field, moveajust + "3", ways, 1, TetrisEnvironment.Move.Null, beforeEval); //180回転 if (TetrisEnvironment.RotateMino(TetrisEnvironment.Move.RightRotate, ref mino, field)) { SearchPattern(mino, field, moveajust + "33", ways, 2, TetrisEnvironment.Move.Null, beforeEval); } } //左回転 if (TetrisEnvironment.RotateMino(TetrisEnvironment.Move.LeftRotate, ref minoclone, field)) { SearchPattern(minoclone, field, moveajust + "4", ways, 1, TetrisEnvironment.Move.Null, beforeEval); } //ネクストが残っていたら if (!CheckNextIsEmpty(nexts)) { var nexts_clone = (int[])nexts.Clone(); int newmino = nexts_clone[0]; for (int i = 0; i < nexts_clone.Length - 1; i++) { nexts_clone[i] = nexts_clone[i + 1]; } nexts_clone[nexts.Length - 1] = (int)TetrisEnvironment.MinoKind.Empty; foreach (var value in ways.Values) { var field_clone = (int[, ])field.Clone(); float eval = NewEvaluation.Evaluate(field_clone, value.mino_positions, beforeEval, true); Search(field_clone, newmino, nexts_clone, ojama, eval, value.way); } } //ネクストなし else { TetrisEnvironment.Way bestkouho = new TetrisEnvironment.Way(); bool first = true; foreach (var value in ways.Values) { //TetrisEnvironment.Print(field, value.mino_positions, true); if (first) { bestkouho = value; first = false; } if (bestkouho.eval < value.eval) { bestkouho = value; } } if (_finalWays == null) { _finalWays = bestkouho; } else { if (_finalWays.Value.eval < bestkouho.eval) { _finalWays = bestkouho; } } } }
static public void Learn() { int threadCount; int threadCount2; ThreadPool.GetMaxThreads(out threadCount, out threadCount2); Console.WriteLine("スレッド数は" + threadCount + "です。"); Console.WriteLine("世代数を入力してください。"); int generationDestination = int.Parse(Console.ReadLine()); Console.WriteLine("学習を開始します。"); int nowGenerationIndex = 0; int index = 0; TetrisEnvironment.MinoKind[] nexts = new TetrisEnvironment.MinoKind[5]; List <TetrisEnvironment.MinoKind> _leftMinoBag = new List <TetrisEnvironment.MinoKind>(); Chromosome[] nowChromosome = new Chromosome[generationDestination]; const int FrameMax = 10800; int[,] field = new int[10, 25]; Random random = new Random(); while (true) { for (int i = 0; i < generationDestination; i++) { float[] values = new float[11]; values[0] = random.Next(-100, 155); values[1] = random.Next(-100, 155); values[2] = random.Next(-100, 155); values[3] = random.Next(-100, 155); values[4] = random.Next(-100, 155); values[5] = random.Next(-100, 155); values[6] = random.Next(-100, 155); values[7] = random.Next(-100, 155); values[8] = random.Next(-100, 155); values[9] = random.Next(-100, 155); values[10] = random.Next(-100, 155); nowChromosome[i].chromosome = CreateChromosome(values); } Label: var values2 = DecodeChromosome(nowChromosome[index].chromosome); NewEvaluation.h_sumofheight = values2[0]; NewEvaluation.h_dekoboko = values2[1]; NewEvaluation.h_holeCount = values2[2]; NewEvaluation.h_holeup1 = values2[3]; NewEvaluation.h_holeup2 = values2[4]; NewEvaluation.h_holeup3 = values2[5]; NewEvaluation.h_holeup4 = values2[6]; NewEvaluation.h_line1 = values2[7]; NewEvaluation.h_line2 = values2[8]; NewEvaluation.h_line3 = values2[9]; NewEvaluation.h_line4 = values2[10]; for (int x = 0; x < 10; x++) { for (int y = 0; y < 25; y++) { field[x, y] = 0; } } Console.Clear(); Console.WriteLine($"{nowGenerationIndex}世代 {index}/{generationDestination}\r\n\r\n"); Console.WriteLine($"現在の評価\r\n" + $"高さ合計:{ NewEvaluation.h_sumofheight}\r\n" + $"1ライン消し:{ NewEvaluation.h_line1}\r\n" + $"2ライン消し:{ NewEvaluation.h_line2}\r\n" + $"3ライン消し:{ NewEvaluation.h_line3}\r\n" + $"4ライン消し:{ NewEvaluation.h_line4}\r\n" + $"でこぼこ:{ NewEvaluation.h_dekoboko}\r\n" + $"穴:{ NewEvaluation.h_holeCount}\r\n" + $"穴の上ミノ1:{ NewEvaluation.h_holeup1}\r\n" + $"穴の上ミノ2:{ NewEvaluation.h_holeup2}\r\n" + $"穴の上ミノ3:{ NewEvaluation.h_holeup3}\r\n" + $"穴の上ミノ4:{ NewEvaluation.h_holeup4}\r\n"); TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); var minotemp = TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); TetrisEnvironment.Mino nowMino = TetrisEnvironment.CreateMino(minotemp); bool canhold = true; TetrisEnvironment.MinoKind?hold = new TetrisEnvironment.MinoKind?(); int frameCount = 0; int score = 0; int ren = 0; bool backtoback = false; while (true) { var way = NewTetrisAI.Find(field, (int)nowMino.kind, Utility.Enum2IntArray(new TetrisEnvironment.MinoKind[1] { nexts[0] }), 0, (int?)hold, canhold); way.way = way.way.Substring(0, way.way.IndexOf('5') + 1); foreach (char action in way.way) { switch (int.Parse(action.ToString())) { case (int)TetrisEnvironment.Move.SoftDrop: if (TetrisEnvironment.CheckValidPosition(field, nowMino.positions, new TetrisEnvironment.Vector2(0, -1))) { nowMino.positions[0] += new TetrisEnvironment.Vector2(0, -1); nowMino.positions[1] += new TetrisEnvironment.Vector2(0, -1); nowMino.positions[2] += new TetrisEnvironment.Vector2(0, -1); nowMino.positions[3] += new TetrisEnvironment.Vector2(0, -1); frameCount += MoveFrame; } break; case (int)TetrisEnvironment.Move.HardDrop: frameCount += MinoSet; canhold = true; int count = 0; while (true) { if (TetrisEnvironment.CheckValidPosition(field, nowMino.positions, new TetrisEnvironment.Vector2(0, count))) { count--; } else { count++; break; } } nowMino.positions[0] += new TetrisEnvironment.Vector2(0, count); nowMino.positions[1] += new TetrisEnvironment.Vector2(0, count); nowMino.positions[2] += new TetrisEnvironment.Vector2(0, count); nowMino.positions[3] += new TetrisEnvironment.Vector2(0, count); foreach (var position in nowMino.positions) { field[position.x, position.y] = 1; } //ミノ消去ANDスコア加算 int clearedCount = TetrisEnvironment.CheckLine(field); if (clearedCount == 4) { backtoback = true; } else { backtoback = false; } if (clearedCount == 0) { ren = 0; } else { ren++; } TetrisEnvironment.UpdateScore(clearedCount, ren, ref score, backtoback); var temp2 = TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); nowMino = TetrisEnvironment.CreateMino(temp2); if (!TetrisEnvironment.CheckValidPosition(field, nowMino.positions, TetrisEnvironment.Vector2.Zero) || frameCount >= FrameMax) { //死亡 if (index == generationDestination - 1) { //次の世代に移行 List <Chromosome> newChromosomes = new List <Chromosome>(); List <Chromosome> tempList = new List <Chromosome>(); nowChromosome[index].eval = score; tempList.AddRange(nowChromosome); tempList.Sort((a, b) => b.eval.CompareTo(a.eval)); score = 0; //エリート方式で追加 for (int i = 0; i < generationDestination - 3; i++) { newChromosomes.Add(new Chromosome(0, tempList[i].chromosome)); } newChromosomes.Add(new Chromosome(0, GeneticAlgorithm.TwoPointCrossOver(tempList[0].chromosome, tempList[1].chromosome))); newChromosomes.Add(new Chromosome(0, GeneticAlgorithm.TwoPointCrossOver(tempList[0].chromosome, tempList[2].chromosome))); if (Probability.Percent(3)) { newChromosomes.Add(new Chromosome(0, GeneticAlgorithm.Mutation(tempList[0].chromosome))); } else { newChromosomes.Add(new Chromosome(0, tempList[0].chromosome)); } nowChromosome = newChromosomes.ToArray(); nowGenerationIndex++; index = 0; } else { nowChromosome[index].eval = score; } index++; goto Label; } break; case (int)TetrisEnvironment.Move.Hold: if (canhold) { canhold = false; if (hold == null) { hold = nowMino.kind; var temp = TetrisEnvironment.UpdateNexts(nexts, _leftMinoBag); nowMino = TetrisEnvironment.CreateMino(temp); } else { var temp = nowMino.kind; nowMino = TetrisEnvironment.CreateMino((TetrisEnvironment.MinoKind)hold); hold = temp; } } break; case (int)TetrisEnvironment.Move.Left: if (TetrisEnvironment.CheckValidPosition(field, nowMino.positions, TetrisEnvironment.Vector2.MinusX1)) { nowMino.positions[0] += TetrisEnvironment.Vector2.MinusX1; nowMino.positions[1] += TetrisEnvironment.Vector2.MinusX1; nowMino.positions[2] += TetrisEnvironment.Vector2.MinusX1; nowMino.positions[3] += TetrisEnvironment.Vector2.MinusX1; frameCount += MoveFrame; } break; case (int)TetrisEnvironment.Move.LeftRotate: if (TetrisEnvironment.RotateMino(TetrisEnvironment.Move.LeftRotate, ref nowMino, field)) { frameCount += MoveFrame; } break; case (int)TetrisEnvironment.Move.Right: if (TetrisEnvironment.CheckValidPosition(field, nowMino.positions, TetrisEnvironment.Vector2.X1)) { nowMino.positions[0] += TetrisEnvironment.Vector2.X1; nowMino.positions[1] += TetrisEnvironment.Vector2.X1; nowMino.positions[2] += TetrisEnvironment.Vector2.X1; nowMino.positions[3] += TetrisEnvironment.Vector2.X1; frameCount += MoveFrame; } break; case (int)TetrisEnvironment.Move.RightRotate: if (TetrisEnvironment.RotateMino(TetrisEnvironment.Move.RightRotate, ref nowMino, field)) { frameCount += MoveFrame; } break; } //TetrisEnvironment.Print(field, nowMino.positions, false); //Console.WriteLine("現在のミノ"); //Console.Write(nowMino.kind + "\r\n"); //Console.WriteLine("ネクスト"); //Console.Write(nexts[0]); //Console.Write(nexts[1]); //Console.Write(nexts[2]); //Console.Write(nexts[3]); //Console.Write(nexts[4] + "\r\n"); //Console.WriteLine("ホールド"); //Console.WriteLine(hold + "\r\n"); //Thread.Sleep(10); ////Console.ReadKey(); } } } }