ebSkola

9.6 Noslēguma projekts: "Krustiņi un nullītes" pārlūkā

Stundas uzdevums: Apvienot visas tēmas prasmes - Streamlit, session_state, ārējie API - pilnvērtīgā tīmekļa spēlē "Krustiņi un nullītes".

SR 2.4.1. Problēmas dekompozīcija SR 2.4.7. Lietotāja saskarne SR 2.4.18. OOP SR 2.4.11. Bibliotēkas

70 min plāns: Ievads un kritēriju pārruna (~10 min) · 1. uzdevums (~15 min) - uzzīmē 3x3 laukumu ar pogām · 2. uzdevums (~25 min) - pievieno gājienus un uzvaras pārbaudi · 3. uzdevums (~20 min) - pievieno statistiku un restartu. Papildu uzdevumu sāc tikai tad, ja pārējie trīs ir gatavi.

Pirms sāc: šis ir 9. tēmas noslēguma projekts. Izmanto Streamlit un st.session_state. Sāc ar tukšu laukumu un tikai tad ķeries pie uzvaras loģikas.

Teorija: Spēles apraksts un arhitektūra

Tava noslēguma spēle ir "Krustiņi un nullītes" pārlūkā, kas izmanto Streamlit, lai abi spēlētāji uz viena datora var spēlēt pret otru (vai pret vienkāršu AI).

Funkcijas:

  • 3×3 spēles laukums ar pogām.
  • Maiņa starp X un O spēlētājiem.
  • Uzvaras pārbaude (8 kombinācijas).
  • Statistika sesijas laikā: kurš ir uzvarējis vairāk reizes.
  • Reset poga jaunai spēlei.
  • Opcija "Spēlēt pret AI" ar minimax algoritmu.

Failu struktūra:

krustini/
├── app.py            # Streamlit galvenā lapa
├── spele.py          # Spele klase ar loģiku
├── ai.py             # AISpeletajs ar minimax
└── requirements.txt

Praktiskie uzdevumi

1. uzdevums -

Uzzīmē 3x3 laukumu ar pogām

Beigās ekrānā būs deviņas pogas trīs rindās.

  1. Izveido failu krustini.py un importē Streamlit.
  2. Izveido st.session_state.laukums = ["" for _ in range(9)], ja tā vēl nav.
  3. Uzraksti ciklu for rinda in range(3):.
  4. Izveido katrā rindā cols = st.columns(3).
  5. Ieliec katrā kolonnā pogu ar unikālu key.
  6. Palaid un pārbaudi, ka redzamas deviņas pogas.

Gatavs, kad: ekrānā ir 3x3 pogu režģis, un nospiežot pogu lapa nekļūdās.

2. uzdevums -

Pievieno gājienus un uzvaras pārbaudi

Beigās spēle pateiks, kurš uzvarējis.

  1. Ieraksti pogas nospiešanas brīdī laukumā X vai O.
  2. Nomaini pašreizējo spēlētāju pēc katra gājiena.
  3. Neļauj pārrakstīt jau aizņemtu rūtiņu.
  4. Izveido sarakstu ar visām 8 uzvaras kombinācijām.
  5. Pārbaudi pēc katra gājiena, vai kāda kombinācija ir aizpildīta ar vienu simbolu.
  6. Izdrukā uzvarētāju ar st.success().
  7. Izspēlē vienu partiju līdz uzvarai.

Gatavs, kad: spēle pareizi paziņo uzvarētāju un neļauj spēlēt uz aizņemtas rūtiņas.

3. uzdevums -

Pievieno statistiku un restartu

Beigās varēs spēlēt vairākas partijas pēc kārtas.

  1. Pievieno session_state skaitītājus X un O uzvarām.
  2. Palielini attiecīgo skaitītāju pēc katras uzvaras.
  3. Izdrukā abus rezultātus lapas augšā.
  4. Pievieno pogu Jauna spēle, kas notīra laukumu, bet ne statistiku.
  5. Pievieno neizšķirta pārbaudi, kad visas rūtiņas aizpildītas.
  6. Izspēlē trīs partijas pēc kārtas.
  7. Veic commit, push un iesniedz krātuves saiti.

Gatavs, kad: pēc trim partijām statistika rāda pareizu uzvaru skaitu, un neizšķirts tiek atpazīts.

Papildu uzdevums - Pievieno vienkāršu AI

Ja pamatdarbs ir gatavs, ļauj spēlēt pret datoru.

  1. Pievieno sānjoslā izvēli Spēlēt pret AI.
  2. Uzraksti funkciju, kas izvēlas nejaušu brīvu rūtiņu.
  3. Izsauc to pēc cilvēka gājiena.
  4. Uzlabo AI: ja tas var uzvarēt vienā gājienā, lai izvēlas tieši to.
  5. Izspēlē partiju pret AI.

Gatavs, kad: AI aizpilda uzvaras rūtiņu, kad tāda ir pieejama, nevis izvēlas nejauši.

Biežākās kļūdas

  • Pogu re-render visu lapu: Streamlit pārpalaiž skriptu - visas pogas jāzina savu key.
  • session_state sajaukts: Piedēvē unikālus atslēgvārdus visiem objektiem (game_id, spele, etc).
  • AI pārāk lēns: Minimax 9 līmeņu dziļumam ir ātrs (8! = 40320 stāvokļi); ja lietotnē kavējās, pievieno alpha-beta pruning.

Koda piemērs

import streamlit as st

class Spele:
    UZVARES = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]

    def __init__(self):
        self.laukums = ["" for _ in range(9)]
        self.pasreizejais = "X"

    def gajiens(self, p):
        if self.laukums[p] or self.uzvaretajs(): return
        self.laukums[p] = self.pasreizejais
        self.pasreizejais = "O" if self.pasreizejais == "X" else "X"

    def uzvaretajs(self):
        for a,b,c in self.UZVARES:
            if self.laukums[a] == self.laukums[b] == self.laukums[c] != "":
                return self.laukums[a]
        return "neizšķirts" if "" not in self.laukums else None

if "spele" not in st.session_state:
    st.session_state.spele = Spele()

s = st.session_state.spele
for r in range(3):
    cols = st.columns(3)
    for c in range(3):
        i = r*3 + c
        cols[c].button(s.laukums[i] or " ", key=i, on_click=s.gajiens, args=(i,))

u = s.uzvaretajs()
if u: st.success(f"Uzvarētājs: {u}")
else: st.info(f"Gājiens: {s.pasreizejais}")

if st.button("↻ Jauna spēle"):
    st.session_state.spele = Spele()
    st.rerun()
3×3 režģis ar pogām;
Pēc katra klikšķa simbols mainās;
Uzvaras paziņojums tiek parādīts.