五目並べでの勝敗判定プログラムはたくさんやり方がある中で、はっきりした答えがネットに見つからなかったので紹介しておきます。
今回僕が作った勝敗判定プログラムは、すべてのマスを一つづつ対象にして、そこから全八方向に一マスづつ進んでいき、同じIDを持っていた場合カウントします??? 説明が難しいので早速コードを見てください。
目次
下準備
まず勝利判定の時に役に立つ関数を先に作ります。
Board.getNextIndex(int place, int direction)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
public int getNextIndex(int place, int direction) { //全8方向(0-7)を指定して、その方向の次のマス目のindexを返す。 //エラーの場合(盤の外)、-1を返す int to_return = -1; switch (direction) { case 0: //上 to_return = place - size; if (to_return < 0) { to_return = -1; } break; case 1: //右上 to_return = place - size + 1; if (to_return < 0 || to_return % size == 0) { to_return = -1; } break; case 2: //右 to_return = place + 1; if ((place + 1) % size == 0) { to_return = -1; } break; case 3: //右下 to_return = place + size + 1; if (to_return >= size * size || to_return % size == 0) { to_return = -1; } break; case 4: //下 to_return = place + size; if (to_return >= size * size) { to_return = -1; } break; case 5: //左下 to_return = place + size - 1; if (to_return >= size * size || place % size == 0) { to_return = -1; } break; case 6: //左 to_return = place - 1; if (place % size == 0) { to_return = -1; } break; case 7: //左上 to_return = place - size - 1; if (to_return < 0 || place % size == 0) { to_return = -1; } break; default: Debug.Log("something went wrong at getNextIndex. Check parameter 'index'. "); break; } return to_return; } |
この関数ではplace と directionを受け取り、そのplaceからdirection方向の隣マスのインデックスを返します。例えば10 x 10 の盤の場合、index 0 (盤の右上の角)の右隣はindex 1、下隣はindex 10、右下は index 11。それ以外の方向は盤の外なので-1を返します。
Board.is_allowed(int place)
この関数はシンプルで、もしも指定されたマスがまだ誰のものでもない場合はtrueを返します。
1 2 3 4 5 6 7 8 9 10 11 12 |
public Boolean is_allowed(int place) { //今回は禁じ手なし。 //6個以上もあり。 //そういうルールを追加したいときはこの関数を使う。 //今回のルールは、「そのマスには誰の駒もない」だけが条件。 if (board[place] == 0) { return true; } return false; } |
何個連続したマスがあるか調べる
Board.checkContinuous(int ID)
この関数ではさっき作ったgetNextIndexをつかって連続したマスがあるか調べ、最大連続数を返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
public int checkContinuous(int ID) { //この関数でIDの駒が最大何連続されているかチェック //盤のサイズによってもチェックするインデックスが変わるため、bruteforce的なことはできない。 //作りながら思ったけど、この関数重い気がする。。。いつか改良しよう。。。 int max = 0; for (int index = 0; index < board.Count; index++) { //IDが一致したマスを一マスづつ見ていく if (board[index] == ID) { //全方向を見る for (int direction = 0; direction < 8; direction++) { int counter = 1; Boolean done = false; int current_index = index; //連続したIDのマスが途切れたらdone = true; while (!done) { current_index = getNextIndex(current_index, direction); if (current_index != -1 && board[current_index] == ID) { counter++; } else { done = true; } if (max < counter) { max = counter; } } } } continue; } if(max >= 5) { is_gameDone = true; } return max; } |
マスの連続が最大5以上の場合は、is_gameDoneをtrueにして、これ以上の入力を却下している。
僕の「Unityで五目並べアプリ作ってみた」の記事から来てくれた人へ
今回作った関数を導入したことでGame.csも少し変更しています。
Game.cs
変更点は、82-93行目。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class Game : MonoBehaviour { public Sprite SpritePlayer1; public Sprite SpritePlayer_1; public Sprite Empty; Vector3 lastPositon = new Vector3(); Boolean is_selecting = false; private Board board; public int size = 15; public int ID = -1; // Start is called before the first frame update void Start() { board = new Board(); board.setSize(size); board.initialize(); GetComponent<UIOrganize>().positionBoard(size); drawBoard(size); } // Update is called once per frame void Update() { int index = GetTargetOnClick(); if (index != -1) { Boolean retult = input(index); Debug.Log("continuous: "+ board.checkContinuous(ID*-1)); if (!retult) { Debug.Log("input was not allowed"); } } } public void drawBoard(int size) { //初期化の時用 GameObject tempItem = transform.Find("Canvas").transform.Find("BoardViewport").transform.Find("Board").GetChild(0).gameObject; GameObject parent = transform.Find("Canvas").transform.Find("BoardViewport").transform.Find("Board").gameObject; int counter = 0; for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { GameObject targetItem = Instantiate(tempItem,new Vector3(0f,0f,0f), Quaternion.identity); targetItem.transform.SetParent(parent.transform); targetItem.transform.name = counter.ToString(); counter++; } } Destroy(tempItem); } public void updatePanel(int placement,int ID) { UnityEngine.UI.Image image = transform.Find("Canvas").transform.Find("BoardViewport").transform.Find("Board").GetChild(placement).GetComponent<UnityEngine.UI.Image>(); switch (ID) { case 1: image.sprite = SpritePlayer1; break; case -1: image.sprite = SpritePlayer_1; break; default: image.sprite = Empty; break; } } public Boolean input(int place) { //if文でルール追加 & ゲームが終わってなかったら。。。 if (board.is_allowed(place) && !board.is_gameDone) { board.intput(place, ID); updatePanel(place, ID); nextPlayer(); return true; } return false; } public void nextPlayer() { ID = ID * -1; } int GetTargetOnClick() { //set true when user press down //set false when user moves position //then check if it is true when user lifts finger. if (Input.GetMouseButtonDown(0)) { lastPositon = Input.mousePosition; is_selecting = true; } if (Input.GetMouseButton(0)) { Vector3 currentPositon = Input.mousePosition; float deltaPositon = Vector3.Distance(currentPositon, lastPositon); if (deltaPositon > 10) { //the user decided not to click here is_selecting = false; } } if (Input.GetMouseButtonUp(0) && is_selecting) { PointerEventData pointer = new PointerEventData(EventSystem.current); pointer.position = Input.mousePosition; List<RaycastResult> result = new List<RaycastResult>(); EventSystem.current.RaycastAll(pointer, result); foreach (RaycastResult raycastresult in result) { if (raycastresult.gameObject.transform.parent.transform.name == "Board") { return int.Parse(raycastresult.gameObject.transform.name); } } } if (Input.touchCount > 0) { // タッチ情報の取得 Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { lastPositon = Input.mousePosition; is_selecting = true; } if (touch.phase == TouchPhase.Moved) { is_selecting = false; } if (touch.phase == TouchPhase.Ended && is_selecting) { PointerEventData pointer = new PointerEventData(EventSystem.current); pointer.position = touch.position; List<RaycastResult> result = new List<RaycastResult>(); EventSystem.current.RaycastAll(pointer, result); foreach (RaycastResult raycastresult in result) { Debug.Log(raycastresult.gameObject.transform.parent.transform.name); if (raycastresult.gameObject.transform.parent.transform.name == "Board") { return int.Parse(raycastresult.gameObject.transform.name); } } } } return -1; } } |
Board.cs
変更点はこの記事で追加(記述)した関数のみ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Tracing; using UnityEditor; using UnityEngine; using UnityEngine.EventSystems; public class Board { public int size; private List<int> board = new List<int>(); public Boolean is_gameDone = false; /* * -1 と 1 はユーザーID * 0 は空のマス */ public void setSize(int param) { size = param; } public void initialize() { board.Clear(); for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { board.Add(0); } } } public List<int> getDeepBoard() { //ディープコピー List<int> temp = new List<int>(); int index = 0; for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { temp.Add(board[index]); index++; } } return temp; } public List<int> getShallowBoard() { //シャローコピー return board; } public List<int> intput(int place,int ID) { board[place] = ID; return board; } public Boolean is_allowed(int place) { //今回は禁じ手なし。 //6個以上もあり。 //そういうルールを追加したいときはこの関数を使う。 //今回のルールは、「そのマスには誰の駒もない」だけが条件。 if (board[place] == 0) { return true; } return false; } public int getNextIndex(int place, int direction) { //全8方向(0-7)を指定して、その方向の次のマス目のindexを返す。 //エラーの場合(盤の外)、-1を返す int to_return = -1; switch (direction) { case 0: //上 to_return = place - size; if (to_return < 0) { to_return = -1; } break; case 1: //右上 to_return = place - size + 1; if (to_return < 0 || to_return % size == 0) { to_return = -1; } break; case 2: //右 to_return = place + 1; if ((place + 1) % size == 0) { to_return = -1; } break; case 3: //右下 to_return = place + size + 1; if (to_return >= size * size || to_return % size == 0) { to_return = -1; } break; case 4: //下 to_return = place + size; if (to_return >= size * size) { to_return = -1; } break; case 5: //左下 to_return = place + size - 1; if (to_return >= size * size || place % size == 0) { to_return = -1; } break; case 6: //左 to_return = place - 1; if (place % size == 0) { to_return = -1; } break; case 7: //左上 to_return = place - size - 1; if (to_return < 0 || place % size == 0) { to_return = -1; } break; default: Debug.Log("something went wrong at getNextIndex. Check parameter 'index'. "); break; } return to_return; } public int checkContinuous(int ID) { //この関数でIDの駒が最大何連続されているかチェック //盤のサイズによってもチェックするインデックスが変わるため、bruteforce的なことはできない。 //作りながら思ったけど、この関数重い気がする。。。いつか改良しよう。。。 int max = 0; for (int index = 0; index < board.Count; index++) { //IDが一致したマスを一マスづつ見ていく if (board[index] == ID) { //全方向を見る for (int direction = 0; direction < 8; direction++) { int counter = 1; Boolean done = false; int current_index = index; //連続したIDのマスが途切れたらdone = true; while (!done) { current_index = getNextIndex(current_index, direction); if (board[current_index] == ID) { counter++; } else { done = true; } if (max < counter) { max = counter; } } } } continue; } if(max >= 5) { is_gameDone = true; } return max; } } |
UIOrganize.cs
変更点なし。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class UIOrganize : MonoBehaviour { public void positionBoard(int size) { RectTransform rectTransformContent = transform.Find("Canvas").transform.Find("BoardViewport").transform.Find("Board").transform.gameObject.GetComponent<RectTransform>(); rectTransformContent.sizeDelta = new Vector2(size*100, size * 100); GridLayoutGroup gridLayoutGroupContent = transform.Find("Canvas").transform.Find("BoardViewport").transform.Find("Board").transform.gameObject.GetComponent<GridLayoutGroup>(); gridLayoutGroupContent.constraint = GridLayoutGroup.Constraint.FixedColumnCount; gridLayoutGroupContent.constraintCount = size; } } |