' ============================================================================
'  PLC-Ristinolla Extreme  -  WinCC Advanced / Comfort (TIA Portal V16+)
'  SAK Service Oy, oma toteutus. Vapaa käyttää omissa projekteissa, omalla vastuulla.
'  HUOM: kirjoitettu WinCC Advanced -rajapinnan mukaan, mutta ei testattu oikeassa
'  paneelissa. Kokeile ensin simulaattorissa.
'
'  HMI-muuttujat (sisäiset, "Internal tag"):
'    RN_Cell    Array[0..80] of Int   ruudut: 0 tyhjä, 1 = X, 2 = O  (indeksi = moduuli*9 + paikka)
'    RN_Macro   Array[0..8]  of Int   moduulit: 0 auki, 1 X voitti, 2 O voitti, 3 tasan
'    RN_Next    Int   moduuli johon seuraavaksi pelataan, -1 = vapaa valinta
'    RN_Turn    Int   1 = X, 2 = O
'    RN_Winner  Int   0 kesken, 1 X, 2 O, 3 tasapeli
'    RN_Mode    Int   0 = kaksi pelaajaa, 1 = pelaa PLC:tä vastaan (PLC on O)
'    RN_Last    Int   viimeisin siirto (korostukseen), -1 = ei ole
'    RN_Msg     WString  tilarivi
'
'  Luo jokainen alla oleva osio omaksi VB-skriptikseen (Scripts > VB scripts > Add new
'  VB function). Otsikkorivi kertoo nimen, tyypin ja parametrit.
' ============================================================================


' ---------------------------------------------------------------------------
' Skripti: RN_NewGame      Tyyppi: Sub      Parametrit: -
' Kutsu: näytön "Loaded"-tapahtuma ja Uusi peli -painike
' ---------------------------------------------------------------------------
Sub RN_NewGame()
    Dim k
    Randomize
    For k = 0 To 80
        SmartTags("RN_Cell")(k) = 0
    Next
    For k = 0 To 8
        SmartTags("RN_Macro")(k) = 0
    Next
    SmartTags("RN_Next") = -1
    SmartTags("RN_Turn") = 1
    SmartTags("RN_Winner") = 0
    SmartTags("RN_Last") = -1
    SmartTags("RN_Msg") = "X aloittaa - valitse mikä tahansa ruutu"
End Sub


' ---------------------------------------------------------------------------
' Skripti: RN_Win3         Tyyppi: Function (Bool)   Parametrit: b (Int), p (Int)
' Onko pelaajalla p kolmen suora moduulissa b (0..8). b = 9 tarkistaa koko telineen.
' ---------------------------------------------------------------------------
Function RN_Win3(b, p)
    Dim L, k, a0, a1, a2
    L = Array(0,1,2, 3,4,5, 6,7,8, 0,3,6, 1,4,7, 2,5,8, 0,4,8, 2,4,6)
    RN_Win3 = False
    For k = 0 To 21 Step 3
        If b = 9 Then
            a0 = SmartTags("RN_Macro")(L(k))
            a1 = SmartTags("RN_Macro")(L(k + 1))
            a2 = SmartTags("RN_Macro")(L(k + 2))
        Else
            a0 = SmartTags("RN_Cell")(b * 9 + L(k))
            a1 = SmartTags("RN_Cell")(b * 9 + L(k + 1))
            a2 = SmartTags("RN_Cell")(b * 9 + L(k + 2))
        End If
        If a0 = p And a1 = p And a2 = p Then
            RN_Win3 = True
            Exit Function
        End If
    Next
End Function


' ---------------------------------------------------------------------------
' Skripti: RN_Full         Tyyppi: Function (Bool)   Parametrit: b (Int)
' ---------------------------------------------------------------------------
Function RN_Full(b)
    Dim k
    RN_Full = True
    For k = 0 To 8
        If SmartTags("RN_Cell")(b * 9 + k) = 0 Then
            RN_Full = False
            Exit Function
        End If
    Next
End Function


' ---------------------------------------------------------------------------
' Skripti: RN_Legal        Tyyppi: Function (Bool)   Parametrit: i (Int)
' ---------------------------------------------------------------------------
Function RN_Legal(i)
    Dim b, nx
    RN_Legal = False
    If SmartTags("RN_Winner") <> 0 Then Exit Function
    If i < 0 Or i > 80 Then Exit Function
    If SmartTags("RN_Cell")(i) <> 0 Then Exit Function
    b = i \ 9
    If SmartTags("RN_Macro")(b) <> 0 Then Exit Function
    nx = SmartTags("RN_Next")
    If nx >= 0 Then
        If SmartTags("RN_Macro")(nx) = 0 And nx <> b Then Exit Function
    End If
    RN_Legal = True
End Function


' ---------------------------------------------------------------------------
' Skripti: RN_CanWin       Tyyppi: Function (Bool)   Parametrit: b (Int), p (Int)
' Voiko p voittaa moduulin b yhdellä siirrolla.
' ---------------------------------------------------------------------------
Function RN_CanWin(b, p)
    Dim k, w
    RN_CanWin = False
    If SmartTags("RN_Macro")(b) <> 0 Then Exit Function
    For k = 0 To 8
        If SmartTags("RN_Cell")(b * 9 + k) = 0 Then
            SmartTags("RN_Cell")(b * 9 + k) = p
            w = RN_Win3(b, p)
            SmartTags("RN_Cell")(b * 9 + k) = 0
            If w Then
                RN_CanWin = True
                Exit Function
            End If
        End If
    Next
End Function


' ---------------------------------------------------------------------------
' Skripti: RN_Apply        Tyyppi: Sub      Parametrit: i (Int)
' Tekee siirron vuorossa olevalle pelaajalle (laillisuus tarkistettu jo).
' ---------------------------------------------------------------------------
Sub RN_Apply(i)
    Dim p, b, c, k, x, o, allDone, names
    names = Array("", "X", "O")
    p = SmartTags("RN_Turn")
    b = i \ 9
    c = i Mod 9
    SmartTags("RN_Cell")(i) = p
    SmartTags("RN_Last") = i
    If RN_Win3(b, p) Then
        SmartTags("RN_Macro")(b) = p
    ElseIf RN_Full(b) Then
        SmartTags("RN_Macro")(b) = 3
    End If

    If RN_Win3(9, p) Then
        SmartTags("RN_Winner") = p
    Else
        allDone = True : x = 0 : o = 0
        For k = 0 To 8
            If SmartTags("RN_Macro")(k) = 0 Then allDone = False
            If SmartTags("RN_Macro")(k) = 1 Then x = x + 1
            If SmartTags("RN_Macro")(k) = 2 Then o = o + 1
        Next
        ' Twist: jos teline täyttyy ilman kolmen suoraa, eniten moduuleja voittanut voittaa
        If allDone Then
            If x > o Then
                SmartTags("RN_Winner") = 1
            ElseIf o > x Then
                SmartTags("RN_Winner") = 2
            Else
                SmartTags("RN_Winner") = 3
            End If
        End If
    End If

    If SmartTags("RN_Macro")(c) = 0 Then
        SmartTags("RN_Next") = c
    Else
        SmartTags("RN_Next") = -1
    End If
    SmartTags("RN_Turn") = 3 - p

    Select Case SmartTags("RN_Winner")
        Case 1, 2
            SmartTags("RN_Msg") = names(SmartTags("RN_Winner")) & " voitti pelin!"
        Case 3
            SmartTags("RN_Msg") = "Tasapeli"
        Case Else
            If SmartTags("RN_Next") = -1 Then
                SmartTags("RN_Msg") = names(3 - p) & ":n vuoro - vapaa valinta"
            Else
                SmartTags("RN_Msg") = names(3 - p) & ":n vuoro - moduuli " & (SmartTags("RN_Next") + 1)
            End If
    End Select
End Sub


' ---------------------------------------------------------------------------
' Skripti: RN_Score        Tyyppi: Function (Int)   Parametrit: i (Int)
' PLC-vastustajan siirron pisteytys.
' ---------------------------------------------------------------------------
Function RN_Score(i)
    Dim p, op, b, c, v, wonB, closedB, nb, free
    p = SmartTags("RN_Turn") : op = 3 - p
    b = i \ 9 : c = i Mod 9 : v = 0

    SmartTags("RN_Cell")(i) = op                          ' estääkö siirto vastustajan voiton?
    If RN_Win3(b, op) Then v = v + 150

    SmartTags("RN_Cell")(i) = p
    wonB = RN_Win3(b, p)
    closedB = wonB Or RN_Full(b)
    If wonB Then
        v = v + 200
        SmartTags("RN_Macro")(b) = p                      ' voittaako koko pelin?
        If RN_Win3(9, p) Then v = v + 100000
        SmartTags("RN_Macro")(b) = 0
    End If

    nb = c
    free = (SmartTags("RN_Macro")(nb) <> 0)
    If nb = b And closedB Then free = True
    If free Then
        v = v - 120                                       ' vapaa valinta vastustajalle
    ElseIf RN_CanWin(nb, op) Then
        v = v - 180                                       ' vastustaja voittaisi seuraavan moduulin
    End If
    SmartTags("RN_Cell")(i) = 0

    If c = 4 Then
        v = v + 6
    ElseIf (c Mod 2) = 0 Then
        v = v + 3
    End If
    RN_Score = v + Int(Rnd * 4)
End Function


' ---------------------------------------------------------------------------
' Skripti: RN_PlcMove      Tyyppi: Sub      Parametrit: -
' ---------------------------------------------------------------------------
Sub RN_PlcMove()
    Dim i, s, best, bestI
    best = -999999 : bestI = -1
    For i = 0 To 80
        If RN_Legal(i) Then
            s = RN_Score(i)
            If s > best Then
                best = s
                bestI = i
            End If
        End If
    Next
    If bestI >= 0 Then RN_Apply bestI
End Sub


' ---------------------------------------------------------------------------
' Skripti: RN_Click        Tyyppi: Sub      Parametrit: idx (Int)
' Kutsu: jokaisen ruudun Click-tapahtuma, parametrina ruudun numero 0..80
' ---------------------------------------------------------------------------
Sub RN_Click(idx)
    If Not RN_Legal(idx) Then Exit Sub
    RN_Apply idx
    If SmartTags("RN_Mode") = 1 And SmartTags("RN_Winner") = 0 And SmartTags("RN_Turn") = 2 Then
        RN_PlcMove
    End If
End Sub
