#Requires AutoHotkey v2.0 position := map(1, map(1, "//////")) value := map(1, map(1, 0)) before := map(1, map(1, "")) history := [] HistoryIndex := 0 column := [] column.length := 7 row := [] ; __ horizontal rows row.push(["11", "21", "31", "41", "51", "61", "71"]) row.push(["12", "22", "32", "42", "52", "62", "72"]) row.push(["13", "23", "33", "43", "53", "63", "73"]) row.push(["14", "24", "34", "44", "54", "64", "74"]) row.push(["15", "25", "35", "45", "55", "65", "75"]) row.push(["16", "26", "36", "46", "56", "66", "76"]) ; | vertical rows row.push(["11", "12", "13", "14", "15", "16"]) row.push(["21", "22", "23", "24", "25", "26"]) row.push(["31", "32", "33", "34", "35", "36"]) row.push(["41", "42", "43", "44", "45", "46"]) row.push(["51", "52", "53", "54", "55", "56"]) row.push(["61", "62", "63", "64", "65", "66"]) row.push(["71", "72", "73", "74", "75", "76"]) ; / diagonal rows, min. 4 stone row.push(["13", "24", "35", "46"]) row.push(["12", "23", "34", "45", "56"]) row.push(["11", "22", "33", "44", "55", "66"]) row.push(["21", "32", "43", "54", "65", "76"]) row.push(["31", "42", "53", "64", "75"]) row.push(["41", "52", "63", "74"]) ; \ diagonal rows, min. 4 stone row.push(["14", "23", "32", "41"]) row.push(["15", "24", "33", "42", "51"]) row.push(["16", "25", "34", "43", "52", "61"]) row.push(["26", "35", "44", "53", "62", "71"]) row.push(["36", "45", "54", "63", "72"]) row.push(["46", "55", "64", "73"]) bold := "" MyGui := gui() MyGui.title := "connect 4" MyGui.BackColor := "4040FF" MyGui.setfont("s16") loop 7 { if (a_index = 1) button := MyGui.add("button", "x15 w40 h40 vdrop1", chr(8595)) else button := MyGui.add("button", "xp+60 w40 h40 vdrop" a_index, chr(8595)) button.OnEvent("click", MyTurn.bind(a_index)) } MyGui.setfont("cwhite s40") loop 7 { x := a_index loop 6 { y := a_index MyGui.add("text", "x" 6+(x-1)*60 " y" 8+(7-y)*50 " w60 h60 vstone" x y " +center", chr(11044)) } } MyGui.setfont("s12") button := MyGui.add("button", "x15 y380 vgo", "go!") button.OnEvent("click", go) button := MyGui.add("button", "x+20 vback", "back") button.OnEvent("click", back) button := MyGui.add("button", "x+20 vNewGame", "new game") button.OnEvent("click", NewGame) MyGui.Add("DropDownList", "x+40 w70 vlevel choose3", ["level 1", "level 2", "level 3", "level 4"]) MyGui.show("w430 h430") pos := map() loop 7 pos["drop" a_index] := [15+(a_index-1)*60, 15, 40, 40, 16] loop 7 { x := a_index loop 6 { y := a_index pos["stone" x y] := [6+(x-1)*60, 8+(7-y)*50, 60, 60, 40] } } pos["go"] := [15, 380, 38, 32, 12] pos["back"] := [73, 380, 50, 32, 12] pos["NewGame"] := [143, 380, 94, 32, 12] pos["level"] := [277, 380, 70, 32, 12] ; pos["level"][4] must be a number, but it has no effect on the DropDownList. resize := [1, 1.2, 1.4, 1.7, 2, 2.4] MySize := 1 MyGui.OnEvent("close", MyGuiClose) MyGuiClose(*) { exitapp } return ;---------------------------------------------------------------------------------------- #HotIf WinActive("ahk_id " MyGui.Hwnd) ^+:: { global for , n in resize if (n > MySize) { MySize := n for element, xywhs in pos { MyGui[element].move(xywhs[1]*n, xywhs[2]*n, xywhs[3]*n, xywhs[4]*n) MyGui[element].setfont("s" xywhs[5]*n) } MyGui.show("w" 430*n "h" 430*n) break } } ^-:: { global for i, n in resize if (n = MySize and i > 1) { n := resize[i-1] MySize := n for element, xywhs in pos { MyGui[element].move(xywhs[1]*n, xywhs[2]*n, xywhs[3]*n, xywhs[4]*n) MyGui[element].setfont("s" xywhs[5]*n) } MyGui.show("w" 430*n "h" 430*n) break } } ;---------------------------------------------------------------------------------------- MyTurn(c,*) { global if (bold != "") { MyGui["button" bold].setfont("norm") bold := "" } v := CalculateValue(position[1][1], 1) if v = 999 { msgbox "Yellow has won!" return } if v = -999 { msgbox "Red has won!" return } if drop(c) { v := CalculateValue(position[1][1], 1) if (v = 999 or v = -999) { loop 3 { sleep 400 loop 4 MyGui["stone" connect4[a_index]].setfont("cwhite") sleep 400 loop 4 MyGui["stone" connect4[a_index]].setfont("c" connect4[5]) } } else go } } ;---------------------------------------------------------------------------------------- go(*) { global if (bold != "") { MyGui["button" bold].setfont("norm") bold := "" } v := CalculateValue(position[1][1], 1) if v = 999 { msgbox "Yellow has won!" return } if v = -999 { msgbox "Red has won!" return } level := MyGui["level"].text level := SubStr(level, -1) CalculateUpTo := 7**level loop 20 ; levels { l1 := a_index l2 := (l1 + 1) position[l2] := map() before[l2] := map() value[l2] := map() n2 := 0 for n1, pos in position[l1] ; positions { value[l1][n1] := CalculateValue(pos, 1) if (value[l1][n1] = 999 or value[l1][n1] = -999) { if (l1 = 2) { loop parse, position[1][1], "/" if (a_index <= n1 and StrLen(a_loopfield) = 6) n1 += 1 drop(n1) MyGui["button" n1].setfont("bold") bold := n1 loop 3 { sleep 400 loop 4 MyGui["stone" connect4[a_index]].setfont("cwhite") sleep 400 loop 4 MyGui["stone" connect4[a_index]].setfont("c" connect4[5]) } return } continue } loop parse, position[l1][n1], "/" ; columns column[a_index] := a_loopfield loop 7 if (StrLen(column[a_index]) < 6) { column[a_index] .= turn(position[l1][n1]) n2 += 1 position[l2][n2] := column[1] "/" column[2] "/" column[3] "/" column[4] "/" column[5] "/" column[6] "/" column[7] before[l2][n2] := n1 value[l2][n2] := "" column[a_index] := SubStr(column[a_index], 1, -1) } } if (n2 = 0 or n2 > CalculateUpTo) break } if (l2 = 2) ; game over? return loop l2-2 ; levels { for n2, pos in position[l2] ; positions { n1 := before[l2][n2] if (value[l2][n2] = "") value[l2][n2] := CalculateValue(pos, 0) if (value[l1][n1] = "") value[l1][n1] := value[l2][n2] else if (turn(position[l1][n1]) = "O" and value[l2][n2] > value[l1][n1] or turn(position[l1][n1]) = "X" and value[l2][n2] < value[l1][n1]) value[l1][n1] := value[l2][n2] } for i, v in value[l1] if (v != "") { if (v > 900) value[l1][i] -= 3 else if (v < -900) value[l1][i] += 3 } l1 -= 1 l2 -= 1 } BestList := [] BestListString := "" loop value[2].count { best := "" for i, v in value[2] if not InStr(BestListString, i) if (best = "" or turn(position[1][1]) = "O" and v > value[2][best] or turn(position[1][1]) = "X" and v < value[2][best]) best := i if (BestList.length = 0 or abs(value[2][best]-value[2][BestList[1]]) < 3) { BestList.push(best) BestListString .= best } else break } best := BestList[random(1, BestList.length)] loop parse, position[1][1], "/" if (a_index <= best and StrLen(a_loopfield) = 6) best += 1 drop(best) MyGui["button" best].setfont("bold") bold := best } ;---------------------------------------------------------------------------------------- ^z:: back(*) { global if (HistoryIndex > 0) { if (bold != "") { MyGui["button" bold].setfont("norm") bold := "" } MyGui["stone" history[HistoryIndex][1]].setfont("cwhite") HistoryIndex -= 1 if (HistoryIndex > 0) position[1][1] := history[HistoryIndex][3] else position[1][1] := "//////" } } ^y:: { global if (HistoryIndex < history.length) { HistoryIndex += 1 mem := history[HistoryIndex] MyGui["stone" mem[1]].setfont("c" mem[2]) position[1][1] := mem[3] } } ;---------------------------------------------------------------------------------------- NewGame(*) { global if (bold != "") { MyGui["button" bold].setfont("norm") bold := "" } for , mem in history MyGui["stone" mem[1]].setfont("cwhite") history := [] HistoryIndex := 0 position[1][1] := "//////" } ;---------------------------------------------------------------------------------------- CalculateValue(position, only4) { global connect4 stone := map() loop parse, position, "/" { x := a_index loop 6 { y := a_index stone[x y] := SubStr(a_loopfield, y, 1) if (stone[x y] = "") stone[x y] := "-" } } connect := [] loop 25 { MyConnect := "" for , xy in row[a_index] MyConnect .= stone[xy] connect.push(MyConnect) } if only4 { loop 25 { if InStr(connect[a_index], "OOOO") { i1 := InStr(connect[a_index], "OOOO") connect4 := [row[a_index][i1], row[a_index][i1+1], row[a_index][i1+2], row[a_index][i1+3], "FFDD00"] return 999 } if InStr(connect[a_index], "XXXX") { i1 := InStr(connect[a_index], "XXXX") connect4 := [row[a_index][i1], row[a_index][i1+1], row[a_index][i1+2], row[a_index][i1+3], "red"] return -999 } } return "" ; not yet valued } else { v := 0 loop 25 { if InStr(connect[a_index], "OOOO") return 999 if InStr(connect[a_index], "XXXX") return -999 if InStr(connect[a_index], "-OOO-") v += 100 if InStr(connect[a_index], "OOO-") or InStr(connect[a_index], "OO-O") or InStr(connect[a_index], "O-OO") or InStr(connect[a_index], "-OOO") v += 40 if InStr(connect[a_index], "OO--") or InStr(connect[a_index], "-OO-") or InStr(connect[a_index], "--OO") or InStr(connect[a_index], "O-O-") or InStr(connect[a_index], "-O-O") or InStr(connect[a_index], "O--O") v += 10 if InStr(connect[a_index], "-XXX-") v -= 100 if InStr(connect[a_index], "XXX-") or InStr(connect[a_index], "XX-X") or InStr(connect[a_index], "X-XX") or InStr(connect[a_index], "-XXX") v -= 40 if InStr(connect[a_index], "XX--") or InStr(connect[a_index], "-XX-") or InStr(connect[a_index], "--XX") or InStr(connect[a_index], "X-X-") or InStr(connect[a_index], "-X-X") or InStr(connect[a_index], "X--X") v -= 10 } for xy, MyStone in stone if (MyStone != "-") { x := SubStr(xy, 1, 1) if (x = 4) v += MyStone="O" ? 10 : -10 else if (x = 3 or x = 5) v += MyStone="O" ? 2 : -2 else if (x = 2 or x = 6) v += MyStone="O" ? 1 : -1 } return v } } ;---------------------------------------------------------------------------------------- drop(c) { global loop parse, position[1][1], "/" column[a_index] := a_loopfield if (StrLen(column[c]) = 6) return 0 stone := turn(position[1][1]) cStone := stone="O" ? "FFDD00" : "red" loop 6-StrLen(column[c]) { MyGui["stone" c 7-a_index].setfont("c" cStone) if (a_index > 1) MyGui["stone" c 8-a_index].setfont("cwhite") sleep 60 } column[c] .= stone position[1][1] := column[1] "/" column[2] "/" column[3] "/" column[4] "/" column[5] "/" column[6] "/" column[7] if (history.length > HistoryIndex) loop history.length-HistoryIndex history.pop() history.push([c StrLen(column[c]), cStone, position[1][1]]) HistoryIndex += 1 return 1 } ;---------------------------------------------------------------------------------------- turn(position) { return mod(StrLen(position), 2)=0 ? "O" : "X" }