Option _Explicit ' One Key Connect 4 (8x8) Halloween Style - bplus 2021-10-19 Const SQ
= 60 ' square or grid cell Const NumCols
= 8 ' number of columns Const NumRows
= 8 ' you guessed it Const NCM1
= NumCols
- 1 ' NumCols minus 1 Const NRM1
= NumRows
- 1 ' you can guess surely Const SW
= SQ
* (NumCols
+ 2) ' screen width Const SH
= SQ
* (NumRows
+ 3) ' screen height Const P
= 1 ' Player is 1 on grid Const AI
= -1 ' AI is -1 on grid Const XO
= SQ
' x offset for grid Const YO
= 3 * SQ
' y offset for grid
ReDim Shared Grid
(NCM1
, NRM1
) ' 0 = empty P=1 for Player, AI=-1 for AI so -4 is win for AI.. DX(0) = 1: DY(0) = 0 ': DString$(0) = "East"
DX(1) = 1: DY(1) = 1 ': DString$(1) = "South East"
DX(2) = 0: DY(2) = 1 ': DString$(2) = "South"
DX(3) = -1: DY(3) = 1 ': DString$(3) = "South West"
DX(4) = -1: DY(4) = 0 ': DString$(4) = "West"
DX(5) = -1: DY(5) = -1 ': DString$(5) = "North West"
DX(6) = 0: DY(6) = -1 ': DString$(6) = "North"
DX(7) = 1: DY(7) = -1 ' : DString$(7) = "North East"
ReDim Shared Scores
(NCM1
) ' rating column for AI and displaying them ReDim Shared AIX
, AIY
' last move of AI for highlighting in display ReDim Shared WinX
, WinY
, WinD
' display Winning Connect 4 ReDim Shared GameOn
, Turn
, GoFirst
, PlayerLastMoveCol
, PlayerLastMoveRow
, MoveNum
' game tracking Dim Shared sx
, pr
' for pumpkin recursion shifty eyes and pumkin radius Dim place
, k$
, t
, r
, s$
, d
, temp&
, target
, y
, delaid
Create_Board
_Title "One Key Connect 4 (8x8) Halloween Style #3" d = 1
sx = sx + d
If sx
> 10 Then d
= -d: sx
= 10 If sx
< -10 Then d
= -d: sx
= -10 GameOn = -1: GoFirst = AI: Turn = AI: MoveNum = 0
ShowGrid
place = -1
pr = (SQ - 2) / 2
t
= Timer: place
= place
+ 1 If place
>= NumCols
Then place
= -1 Else ' watch out for midnight! t
= Timer ' wait a little longer r = GetOpenRow(place)
y = 2 * SQ + SQ / 2
target = r * SQ + YO + SQ / 2
delaid = 6
y = y + 1
ShowGrid
pumpkin 0, place * SQ + XO + SQ / 2, y, pr, 2
delaid = delaid * 2
Grid(place, r) = P: Turn = AI: PlayerLastMoveCol = place: PlayerLastMoveRow = r: MoveNum = MoveNum + 1
place = -1 ' reset back to hold area
AIMove
Turn
= P: MoveNum
= MoveNum
+ 1: t
= Timer ShowGrid
s$ = "Holding area, press spacebar until over column to play."
s$
= "Press Spacebar, if don't want to play" + Str$(place
) + " column." dp XO + 1, YO - SQ - 16 + 1, s$
pumpkin 0, place * SQ + XO + SQ / 2, 2 * SQ + SQ / 2, pr, 2
' What this sub does in English:
' This sub assigns the value to playing each column, then plays the best value with following caveats:
' + If it finds a winning move, it will play that immediately.
' + If it finds a spoiler move, it will play that if no winning move was found.
' + It will poisen the column's scoring, if opponent can play a winning move if AI plays this column,
' but it might be the only legal move left. We will have to play it if no better score was found.
Dim c
, r
, d
, cntA
, cntP
, bestScore
, startR
, startC
, iStep
, test
, goodF
, i
Dim openRow
(NCM1
) ' find open rows once ReDim Scores
(NCM1
) ' evaluate each column's potential AIX = -1: AIY = -1 ' set these when AI makes move, they are signal to display procedure AI's move.
openRow(c) = GetOpenRow(c)
r = openRow(c)
For d
= 0 To 3 ' 4 directions to build connect 4's that use cell c, r startC = c + -3 * DX(d): startR = r + -3 * DY(d)
For i
= 0 To 3 ' here we backup from the potential connect 4 in opposite build direction of c, r cntA = 0: cntP = 0: goodF = -1 ' reset counts and flag for good connect 4
'from this start position run 4 steps forward to count all connects involving cell c, r
For iStep
= 0 To 3 ' process a potential connect 4 test = GR(startC + i * DX(d) + iStep * DX(d), startR + i * DY(d) + iStep * DY(d))
If test
= NumRows
Then goodF
= 0:
Exit For 'cant get connect4 from here If test
= AI
Then cntA
= cntA
+ 1 If test
= P
Then cntP
= cntP
+ 1 If goodF
Then 'evaluate the Legal Connect4 we could build with c, r If cntA
= 3 Then ' we are done! winner! AIX = c: AIY = r ' <<< this is the needed 4th cell to win tell ShowGrid last cell
Grid(c, r) = AI ' <<< this is the needed 4th cell to win, add to grid this is AI move
Scores(c) = 1000
AIX = c: AIY = r 'set the move but don't exit there might be a winner
Scores(c) = 900
Scores(c) = Scores(c) + 8
Scores(c) = Scores(c) + 4 'play this to connect 3 or prevent player from Connect 3
Scores(c) = Scores(c) + 4
Scores(c) = Scores(c) + 2 ' play this to connect 2 or prevent player from Connect 2
ElseIf (cntA
= 0 And cntP
= 0) Then ' OK it's not a wasted move as it has potential for connect4 Scores(c) = Scores(c) + 1 ' this is good move because this can still be a Connect 4
If Stupid
(c
, r
) Then Scores
(c
) = -1000 + Scores
(c
) ' poison because if played the human can win If AIX
<> -1 Then ' we found a spoiler so move there since we haven't found a winner Grid(AIX, AIY) = AI ' make move on grid and done!
If GetOpenRow
(PlayerLastMoveCol
) < NumRows
Then 'all things being equal play on top of player's last move bestScore = Scores(PlayerLastMoveCol): AIY = PlayerLastMoveRow - 1: AIX = PlayerLastMoveCol
bestScore = -1000 ' a negative score indicates that the player can beat AI with their next move
r = openRow(c)
If Scores
(c
) > bestScore
Then bestScore
= Scores
(c
): AIY
= r: AIX
= c
Grid(AIX, AIY) = AI ' make first best score move we found
Else 'We have trouble! Oh but it could be there are no moves!!! ' checkWin is run after every move by AI or Player if there were no legal moves left it should have caught that.
' Just in case it didn't here is an error stop!
dp 2 * 8, 4 * 16, "AI has failed to find a proper move, press any to end..."
Sleep ' <<< pause until user presses a key
GetOpenRow = NumRows 'assume none open
Grid(c, r) = AI
ppr = GetOpenRow(c)
Grid(c, ppr) = P
If CheckWin
= 4 Then Stupid
= -1 Grid(c, ppr) = 0
Grid(c, r) = 0
Function GR
(c
, r
) ' if c, r are out of bounds returns N else returns grid(c, r) ' need to check the grid(c, r) but only if c, r is on the board
If c
< 0 Or c
> NCM1
Or r
< 0 Or r
> NRM1
Then GR
= NumRows
Else GR
= Grid
(c
, r
)
Dim i
, r
, c
, check
, s$
, k$
If MoveNum
<> lastMoveNum
Then ' file newest move Record$
(PlayerLastMoveCol
, PlayerLastMoveRow
) = _Trim$(Str$(MoveNum
)) + " " + "P" Record$
(AIX
, AIY
) = _Trim$(Str$(MoveNum
)) + " " + "A" lastMoveNum = MoveNum
'Line (XO, YO)-Step(NumCols * SQ, NumRows * SQ), &HFF004400, BF
For i
= 0 To NumCols
'grid Line (SQ
* i
+ XO
, YO
)-Step(0, NumRows
* SQ
), &HFFFFFFFF Line (XO
, SQ
* i
+ YO
)-Step(NumCols
* SQ
, 0), &HFFFFFFFF For r
= NRM1
To 0 Step -1 ''in grid rows are reversed 0 is top row 'Line (c * SQ + XO + 3, r * SQ + YO + 3)-Step(SQ - 6, SQ - 6), &HFF000000, BF
pumpkin 0, c * SQ + XO + SQ / 2, r * SQ + YO + SQ / 2, pr, 2
If c
= AIX
And r
= AIY
Then 'highlite last AI move 'Line (c * SQ + XO + 3, r * SQ + YO + 3)-Step(SQ - 6, SQ - 6), &HFF8888FF, BF ' no overlay
Line (c
* SQ
+ XO
, r
* SQ
+ YO
)-Step(SQ
, SQ
), &HFF8888FF, BF
'Line (c * SQ + XO + 3, r * SQ + YO + 3)-Step(SQ - 6, SQ - 6), &HFF4444FF, BF ' no overlay
Line (c
* SQ
+ XO
, r
* SQ
+ YO
)-Step(SQ
, SQ
), &HFF4444FF, BF
drawSpinner c
* SQ
+ XO
+ SQ
/ 2, r
* SQ
+ YO
+ SQ
/ 2, .4, _Pi(-c
/ 8), _RGB32(Rnd * 30 + 40, Rnd * 15 + 20, Rnd * 6 + 10) dp XO
+ c
* SQ
+ (60 - Len(s$
) * 8) / 2, YO
+ SQ
* NumRows
+ 22, s$
'_Display
check = CheckWin
If check
Then 'report end of round ad see if want to play again Line ((WinX
+ i
* DX
(WinD
)) * SQ
+ XO
+ 10, (WinY
+ i
* DY
(WinD
)) * SQ
+ YO
+ 10)-Step(SQ
- 20, SQ
- 20), &HFFFFFF00, B
s$
= Mid$(Record$
(c
, r
), 1, InStr(Record$
(c
, r
), " ") - 1) dp SQ
* c
+ XO
+ (SQ
- Len(s$
) * 8) / 2, SQ
* r
+ YO
+ 22, s$
s$ = " AI is Winner!"
s$ = " Human is Winner!"
s$ = " Board is full, no winner." ' keep Turn the same
dp
(SW
- Len(s$
) * 8) / 2, 2 * 16, s$
s$ = " Play again? press spacebar, escape to quit... "
dp
(SW
- Len(s$
) * 8) / 2, 4 * 16, s$
keywait:
ReDim Grid
(NCM1
, NRM1
), Scores
(NCM1
) Turn = GoFirst: MoveNum = 0
Function CheckWin
' return WinX, WinY, WinD along with +/- 4, returns NumRows if grid full, 0 if no win and grid not full gridFull = NumRows
If Grid
(c
, r
) Then ' check if c starts a row s = 0
s = s + Grid(c + i, r)
If r
> 2 Then ' check if c starts a col s = 0
s = s + Grid(c, r - i)
If r
> 2 And c
< NCM1
- 2 Then 'check if c starts diagonal up to right s = 0
For i
= 0 To 3 ' north east s = s + Grid(c + i, r - i)
If r
> 2 And c
> 2 Then 'check if c starts a diagonal up to left s = 0
For i
= 0 To 3 ' north west s = s + Grid(c - i, r - i)
gridFull = 0 ' at least one enpty cell left
End If 'grid is something CheckWin = gridFull
Sub pumpkin
(dh&
, cx
, cy
, pr
, limit
) Dim lastr
, u
, dx
, i
, tx1
, tx2
, tx3
, ty1
, ty2
, ty3
, ty22
, sxs
'carve this!
fEllipse cx, cy, pr, 29 / 35 * pr
lastr = 2 / 7 * pr
ellipse cx, cy, lastr, 29 / 35 * pr
lastr = .5 * (pr - lastr) + lastr + 1 / 35 * pr
' 'flickering candle light
'Color _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120)
' eye sockets
ftri2 dh&
, cx
- 9 * pr
/ 12, cy
- 2 * pr
/ 12, cx
- 7 * pr
/ 12, cy
- 6 * pr
/ 12, cx
- 3 * pr
/ 12, cy
- 0 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) ftri2 dh&
, cx
- 7 * pr
/ 12, cy
- 6 * pr
/ 12, cx
- 3 * pr
/ 12, cy
- 0 * pr
/ 12, cx
- 2 * pr
/ 12, cy
- 3 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) ftri2 dh&
, cx
+ 9 * pr
/ 12, cy
- 2 * pr
/ 12, cx
+ 7 * pr
/ 12, cy
- 6 * pr
/ 12, cx
+ 3 * pr
/ 12, cy
- 0 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) ftri2 dh&
, cx
+ 7 * pr
/ 12, cy
- 6 * pr
/ 12, cx
+ 3 * pr
/ 12, cy
- 0 * pr
/ 12, cx
+ 2 * pr
/ 12, cy
- 3 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120)
' nose
ftri2 dh&
, cx
, cy
- rand%
(2, 5) * pr
/ 12, cx
- 2 * pr
/ 12, cy
+ 2 * pr
/ 12, cx
+ rand%
(1, 2) * pr
/ 12, cy
+ 2 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120)
' evil grin
ftri2 dh&
, cx
- 9 * pr
/ 12, cy
+ 1 * pr
/ 12, cx
- 7 * pr
/ 12, cy
+ 7 * pr
/ 12, cx
- 6 * pr
/ 12, cy
+ 5 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) ftri2 dh&
, cx
+ 9 * pr
/ 12, cy
+ 1 * pr
/ 12, cx
+ 7 * pr
/ 12, cy
+ 7 * pr
/ 12, cx
+ 6 * pr
/ 12, cy
+ 5 * pr
/ 12, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120)
' moving teeth/talk/grrrr..
u = rand%(4, 8)
dx = pr / u
tx1 = cx - 6 * pr / 12 + (i - 1) * dx
tx2 = tx1 + .5 * dx
tx3 = tx1 + dx
ty1 = cy + 5 * pr / 12
ty3 = cy + 5 * pr / 12
ty2
= cy
+ (4 - Rnd) * pr
/ 12 ty22
= cy
+ (6 + Rnd) * pr
/ 12 ftri2 dh&
, tx1
, ty1
, tx2
, ty2
, tx3
, ty3
, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) ftri2 dh&
, tx1
+ .5 * dx
, ty1
, tx2
+ .5 * dx
, ty22
, tx3
+ .5 * dx
, ty3
, _RGB(Rnd * 55 + 200, Rnd * 55 + 200, 120) 'shifty eyes
If limit
= 3 Then sxs
= sx
Else sxs
= .1 * limit
* sx
pumpkin dh&
, sxs
+ cx
- 5 * pr
/ 12, cy
- 2.5 * pr
/ 12, .15 * pr
, Int(limit
- 1) pumpkin dh&
, sxs
+ cx
+ 5 * pr
/ 12, cy
- 2.5 * pr
/ 12, .15 * pr
, Int(limit
- 1)
scale = yRadius / xRadius
Line (CX
, CY
- yRadius
)-(CX
, CY
+ yRadius
), , BF
y
= scale
* Sqr(xRadius
* xRadius
- x
* x
) Line (CX
+ x
, CY
- y
)-(CX
+ x
, CY
+ y
), , BF
Line (CX
- x
, CY
- y
)-(CX
- x
, CY
+ y
), , BF
scale = yRadius / xRadius: xs = xRadius * xRadius
PSet (CX
, CY
- yRadius
):
PSet (CX
, CY
+ yRadius
) lastx = 0: lasty = yRadius
y
= scale
* Sqr(xs
- x
* x
) Line (CX
+ lastx
, CY
- lasty
)-(CX
+ x
, CY
- y
) Line (CX
+ lastx
, CY
+ lasty
)-(CX
+ x
, CY
+ y
) Line (CX
- lastx
, CY
- lasty
)-(CX
- x
, CY
- y
) Line (CX
- lastx
, CY
+ lasty
)-(CX
- x
, CY
+ y
) lastx = x: lasty = y
rand%
= Int(Rnd * (hi%
- lo%
+ 1)) + lo%
Dim a
, a1
, a2
, x3
, x4
, x5
, x6
, y3
, y4
, y5
, y6
x3
= x1
+ r1
* Cos(a1
): y3
= y1
+ r1
* Sin(a1
) x4
= x1
+ r1
* Cos(a2
): y4
= y1
+ r1
* Sin(a2
) x5
= x2
+ r2
* Cos(a1
): y5
= y2
+ r2
* Sin(a1
) x6
= x2
+ r2
* Cos(a2
): y6
= y2
+ r2
* Sin(a2
) fquad x3, y3, x4, y4, x5, y5, x6, y6, c
Fcirc x1, y1, r1, c
Fcirc x2, y2, r2, c
'need 4 non linear points (not all on 1 line) list them clockwise so x2, y2 is opposite of x4, y4
ftri x1, y1, x2, y2, x4, y4, c
ftri x3, y3, x4, y4, x1, y1, c
prc
= _RGB32(255, 255, 255, 255) mx2 = max + max
_Source tef
'point wont read without this! Line (lasti
, lastj
)-(i
, j
), prc
lasti = i: lastj = j
x = 0
x = x + 1
xleft(y) = x
x = x + 1
x = x + 1
If x
= mx2
Then xright
(y
) = xleft
(y
) Else xright
(y
) = x
If xleft
(y
) <> mx2
Then Line (xleft
(y
) + x0
- max
, y
+ y0
- max
)-(xright
(y
) + x0
- max
, y
+ y0
- max
), c
, BF
Radius
= Abs(R
): RadiusError
= -Radius: X
= Radius: Y
= 0 Line (CX
- X
, CY
)-(CX
+ X
, CY
), C
, BF
RadiusError = RadiusError + Y * 2 + 1
Line (CX
- Y
, CY
- X
)-(CX
+ Y
, CY
- X
), C
, BF
Line (CX
- Y
, CY
+ X
)-(CX
+ Y
, CY
+ X
), C
, BF
X = X - 1
RadiusError = RadiusError - X * 2
Y = Y + 1
Line (CX
- X
, CY
- Y
)-(CX
+ X
, CY
- Y
), C
, BF
Line (CX
- X
, CY
+ Y
)-(CX
+ X
, CY
+ Y
), C
, BF
Dim x1
, x2
, x3
, x4
, y1
, y2
, y3
, y4
, r
, a
, a1
, a2
, lg
, d
, rd
switch = switch + 2
switch
= switch
Mod 16 + 1 r = 10 * scale
x1
= x
+ r
* Cos(heading
): y1
= y
+ r
* Sin(heading
) r = 2 * r 'lg lengths
a
= heading
+ .9 * lg
* _Pi(1 / 5) + (lg
= switch
) * _Pi(1 / 10) a
= heading
- .9 * (lg
- 4) * _Pi(1 / 5) - (lg
= switch
) * _Pi(1 / 10) x2
= x1
+ r
* Cos(a
): y2
= y1
+ r
* Sin(a
) drawLink x1
, y1
, 3 * scale
, x2
, y2
, 2 * scale
, _RGB32(rred
+ 20, ggreen
+ 10, bblue
+ 5) x3
= x2
+ r
* 1.5 * Cos(a1
): y3
= y2
+ r
* 1.5 * Sin(a1
) drawLink x2
, y2
, 2 * scale
, x3
, y3
, scale
, _RGB32(rred
+ 35, ggreen
+ 17, bblue
+ 8) a2
= a1
+ d
* _Pi(1 / 8) * rd
/ 8 x4
= x3
+ r
* 1.5 * Cos(a2
): y4
= y3
+ r
* 1.5 * Sin(a2
) drawLink x3
, y3
, scale
, x4
, y4
, scale
, _RGB32(rred
+ 50, ggreen
+ 25, bblue
+ 12) r = r * .5
Fcirc x1
, y1
, r
, _RGB32(rred
- 20, ggreen
- 10, bblue
- 5) x2
= x1
+ (r
+ 1) * Cos(heading
- _Pi(1 / 12)): y2
= y1
+ (r
+ 1) * Sin(heading
- _Pi(1 / 12)) Fcirc x2, y2, r * .2, &HFF000000
x2
= x1
+ (r
+ 1) * Cos(heading
+ _Pi(1 / 12)): y2
= y1
+ (r
+ 1) * Sin(heading
+ _Pi(1 / 12)) Fcirc x2, y2, r * .2, &HFF000000
r = r * 2
x1
= x
+ r
* .9 * Cos(heading
+ _Pi): y1
= y
+ r
* .9 * Sin(heading
+ _Pi) TiltedEllipseFill
0, x1
, y1
, r
, .7 * r
, heading
+ _Pi, _RGB32(rred
, ggreen
, bblue
)
Line (XO
, YO
)-Step(SQ
* NumCols
, SQ
* NumRows
), _RGB32(40, 20, 10), BF
Fcirc SQ
* x
+ XO
+ SQ
/ 2, SQ
* y
+ YO
+ SQ
/ 2, (SQ
- 2) / 2, _RGB32(0)
Sub dp
(x
, y
, s$
) ' double print white over black to stand out over images