# connect 4
---

---
## 設定函式庫
- 這些是在這個遊戲當中會用到的函式庫
- 在此次專案中,我們會使用到numpy、sys、math,這三個函式庫
- 可在引入時定義函式庫的簡寫,之後呼叫時就可以使用簡寫,例如我們想要將numpy簡寫成np,寫法為
```python
import numpy as np
```
---
## 設定函式庫
```python=
import numpy as np
import sys
import math
```
---
## 創建棋盤
- 在創建棋盤時,我們發現需要用到一個類似二維陣列的結構
- 在numpy這個函式庫中,可以呼叫他並建構出一個二維陣列
- np.zeros:在這個函式就是用來創造一個全部都由0填滿的空間而他後面的括號就是用來決定你這個空間要長甚麼樣子,以及這個0要是怎樣的變數型態
```python=
np.zeros((形狀),(0的型態))
np.zeros(數字,dtype=這邊放你想要0是甚麼型態ex.int、float)
```
---
- 例如若想要創建一個有5行3列的二維陣列,則寫法為
```python
board = np.zeros((5, 3))#創建一個全是0的二維陣列
```
---
## 創建棋盤
- 在此遊戲中,我們需要建構出$6\times 7$的鍵盤,並且將其寫成一自定義函式
```python=
ROW = 6
COL = 7
def create_board():#自定義一個創建棋盤的函式
board = np.zeros((ROW, COL))#創建一個全是0的二維陣列
return board
```
---
## 測試程式碼
- 這段程式碼可以用來測試你現在的棋盤是否有寫對,之後你每寫完一個段落就可以用這段程式碼來測試你的棋盤是否能正常運作
```python=
board = create_board()#建立一個棋盤
print(board)
[[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0.]]
```
---
## 主要的遊戲循環
- 在創建完棋盤後,開始進到遊戲邏輯,每次我們皆需要詢問一個人並讓他選擇要放在哪裡
- 用一個game_over變數來讓程式知道目前遊戲是否結束
- 則這時我們可以用一個turn變數來看目前是誰要出牌,0代表1號玩家出牌,1代表2號玩家出牌
- 每次出完牌之後都要讓1變成0,0變成1,程式碼如下
---
## 主要的遊戲循環
```python
turn = 0
game_over = 1
while game_over == 1:
if turn == 0:
col = int(input("玩家1請選一個數字(0 ~ 6)"))
turn = 1
else:
col = int(input("玩家2請選一個數字(0 ~ 6)"))
turn = 0
```
- 若我們想將交換turn的地方簡短,可寫成turn = 1 - turn,每當turn是1的時候,結束運算就會變成0,相反的若一開始是0,則結束運算變成1
---
## 主要的遊戲循環
- 請注意這裡的col跟前面設棋盤大小的COL不一樣喔
- 搭配前面的create_board,可將程式碼寫成:
```python=
ROW = 6
COL = 7
def create_board():#自定義一個創建棋盤的函式
board = np.zeros((ROW, COL))#創建一個全是0的二維陣列
return board
board = create_board()
game_over = 1
turn = 0#判斷現在輪到誰
while game_over == 1:#若有玩家贏了用來跳出遊戲
if turn == 0:
col = int(input("玩家1請選一個數字(0 ~ 6)"))
else:
col = int(input("玩家2請選一個數字(0 ~ 6)"))
turn = 1-turn
```
---
## 棋子的函式
- 在棋子的運作中,總共分成幾個函式來進行,接下來我們會依序介紹
- drop_piece:在這個函式中,我們要將旗子放入玩家所指定的位置中
- is_valid_location:在這個函式中,我們要檢查玩家所填入的位置是否還有空格
- get_next_open_row:在這個函式中,要找到該列中哪個位置可以放置棋子
---
## 把玩家的棋子填進空格裡
- 在這個函式中,我們需要將棋盤中的(row, col)位置填起來,並且要告知目前是誰出牌(piece),代表這個位置有人出過了
```python=
def drop_piece(board, row, col, piece):#把玩家指定的空格填起來
board[row][col] = piece
```
---
## 檢查玩家所選的那列還有沒有空位
- 因為棋子是從下面開始出,所以我們只要保證第五個位置還沒有人出過,代表這一列還可以放棋子
```python=
def is_valid_location(board, col):#檢查玩家填入的是不是一個有效的位子
return board[5][col] == 0#這裡5也可以寫成ROW-1
```
---
## 確認放的那格的位置
- 如果board[r][col] == 0 就代表這個位置是空的,就可以放置棋子
```python=
def get_next_open_row(board, col):#確認下一個能放的位子
for r in range(ROW):
if board[r][col] == 0:
return r
```
---
## 把剛剛的東西寫到遊戲循環
```python=
while game_over == 1:#若有玩家贏了用來跳出遊戲
#問玩家1
if turn == 0:
col = int(input("玩家1請選一個數字(0 ~ 6)"))
if is_valid_location(board, col):
row = get_next_open_row(board, col)
drop_piece(board, row, col, 1)
#問玩家2
else:
col = int(input("玩家2請選一個數字(0 ~ 6)"))
if is_valid_location(board, col):
row = get_next_open_row(board, col)
drop_piece(board, row, col, 2)
print(board)#程式有沒有寫錯
turn = 1-turn
```
---
## 你的棋盤好像錯了
- 棋子沒有從最下面堆上來
```python=
{0, 0, 0, 0, 0, 0, 0} #col的陣列其實是從這邊開始的,而不是最下面
{0, 0, 0, 0, 0, 0, 0}
{0, 0, 0, 0, 0, 0, 0}
{0, 0, 0, 0, 0, 0, 0}
{0, 0, 0, 0, 0, 0, 0}
{0, 0, 0, 0, 0, 0, 0}
```
---
## 翻轉陣列
- 棋子應該要由下往上排,所以我們需要將陣列上下翻轉
- np.flip:用來翻轉陣列
```python=
np.flip((你要翻轉的陣列名稱),(翻轉參數))
np.flip(a, axis=你想要的參數)
```
- 而這個參數的意義就是要以哪個部份當基準進行翻轉
---
- 舉例我們創建一個叫a的二維陣列,而這個二維陣列裡包含三個一維陣列,而各三個一維陣列裡有各有三個數
```python=
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
a[3][3]
np.flip(a, axis=x)
x=0,代表是最外層(或第0層)[1, 2, 3]、[4, 5, 6]、[7, 8, 9]這三個一維陣列進行翻轉
x=1,代表是第1層[1, 2, 3]、[4, 5, 6]、[7, 8, 9]這三個一維陣列裡面的那三個數字進行翻轉
```
---
## 翻轉棋盤程式碼
```python=
def print_board(board):#用來改變陣列方向
print(np.flip(board, axis=0))#flip就是翻轉陣列
```
---
## 贏的話要終止程式
- 如何判斷某玩家是否贏了
- 贏的條件有,直排連續4個,橫排連續4個,斜排連續4個
- 直排和橫排的寫法還可以理解,那斜排呢?
- 我們可以發現可以從斜率的概念去想,若此直線斜率為1或著是-1且連續4個,那我們也可以判斷此玩家獲勝
---
## 檢查橫線的
- 在此我們要判斷每一行是否存在贏的可能,同時每一行當中又有可能有幾種結果,因為每一行不只4個數字
- 這裡我們要用到巢狀迴圈的寫法,寫法如下:
```python=
#檢查所有橫線
for c in range(COL-3):
for r in range(ROW):
if board[r][c] == piece and board[r][c+1] == piece and board[r][c+2] == piece and board[r][c+3] == piece:
return True
```
---
## 檢查直線的
- 就如同橫排寫法,這裡也有使用巢狀迴圈,寫法如下:
```python=
for c in range(COL):
for r in range(ROW-3):
if board[r][c] == piece and board[r+1][c] == piece and board[r+2][c] == piece and board[r+3][c] == piece:
return True
```
---
## 斜率為正
- 斜率為正的該如何判斷呢?
- 若當前開始找的點為$(i,j)$,則我們要做的就是判斷$(i,j)、(i+1,j+1)、(i+2,j+2)、(i+3,j+3)$這四個點是不是都一樣
```python=
for c in range(COL-3):
for r in range(ROW-3):
if board[r][c] == piece and board[r+1][c+1] == piece and board[r+2][c+2] == piece and board[r+3][c+3] == piece:
return True
```
---
## 斜率為負
- 相反的若要判斷斜率為負,則要尋找$(i,j)、(i-1,j+1)、(i-2,j+2)、(i-3,j+3)$這四個點是不是都一樣
```python=
for c in range(COL-3):
for r in range(3, ROW):
if board[r][c] == piece and board[r-1][c+1] == piece and board[r-2][c+2] == piece and board[r-3][c+3] == piece:
return True
```
---
## 完成程式碼
```python=
import numpy as np
import pygame
import sys
import math
ROW=6
COL=7
def create_board(): #創建棋盤
board = np.zeros((ROW, COL))
return board
def drop_piece(board, row, col, piece):#把玩家指定的空格填起來
board[row][col] = piece
def is_valid_location(board, col):#確認目前還是空的位子
return board[ROW-1][col] == 0
def get_next_open_row(board, col):
for r in range(ROW):
if board[r][col] == 0:
return r
def print_board(board):#用來改變陣列方向
print(np.flip(board, axis=0))#flip就是翻轉陣列
def winning_move(board, piece):#判斷是否有人達成獲勝條件
#檢查所有橫線
for c in range(COL-3):
for r in range(ROW):
if board[r][c] == piece and board[r][c+1] == piece and board[r][c+2] == piece and board[r][c+3] == piece:
return True
#檢查所有直線
for c in range(COL):
for r in range(ROW-3):
if board[r][c] == piece and board[r+1][c] == piece and board[r+2][c] == piece and board[r+3][c] == piece:
return True
#檢查所有斜線
#斜率為正
for c in range(COL-3):
for r in range(ROW-3):
if board[r][c] == piece and board[r+1][c+1] == piece and board[r+2][c+2] == piece and board[r+3][c+3] == piece:
return True
#斜率為負
for c in range(COL-3):
for r in range(3, ROW):
if board[r][c] == piece and board[r-1][c+1] == piece and board[r-2][c+2] == piece and board[r-3][c+3] == piece:
return True
board = create_board()
print_board(board)
turn = 0#用來判斷現在是輪到玩家1還是2
game_over = False
while not game_over:#若有玩家贏了用來跳出遊戲
#問玩家1
if turn == 0:
col = int(input("玩家1請選一個數字(0 ~ 6)"))
if is_valid_location(board, col):
row = get_next_open_row(board, col)
drop_piece(board, row, col, 1)
if winning_move(board,1):
print("恭喜玩家1獲勝!!!")
game_over=True
break
#問玩家2
else:
col = int(input("玩家2請選一個數字(0 ~ 6)"))
if is_valid_location(board, col):
row = get_next_open_row(board, col)
drop_piece(board, row, col, 2)
if winning_move(board,2):
print("恭喜玩家2獲勝!!!")
game_over=True
break
print_board(board)#程式有沒有寫錯
turn+=1
turn%=2
```
{"metaMigratedAt":"2023-06-17T22:02:04.434Z","metaMigratedFrom":"YAML","breaks":true,"slideOptions":"{\"theme\":\"white\"}","title":"connect 4_1","contributors":"[{\"id\":\"4d8ac2d3-29c7-4a8e-a906-e7cdf0999b25\",\"add\":7165,\"del\":631},{\"id\":\"83f5b3e0-d6bd-4eaf-bceb-fc6a8dfdeecf\",\"add\":1171,\"del\":3545},{\"id\":\"0b6a8e0d-3e93-4b99-baa7-fbd8a96f84ef\",\"add\":2,\"del\":2},{\"id\":\"b405d87d-0698-478c-9009-9939bd969d2c\",\"add\":2423,\"del\":81},{\"id\":\"ca056cfc-4faf-46a7-8aa9-ef58b3a12a53\",\"add\":1975,\"del\":736}]"}