Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek |
ProblemNależy opracować algorytm gry
|
Zasady gry
Zastanówmy się nad tym z jak dużym zbiorem mamy do czynienia. Kod kodera
składa się z 4 liter należących do alfabetu
6×6×6×6 = 1296
Jeśli nie znasz kombinatoryki, to wyjaśnienie tego wzoru jest następujące:
Na pierwszej pozycji może pojawić się jedna z 6 liter –
Axxx, Bxxx, Cxxx, Dxxx, Exxx, Fxxx, gdzie x – pozycja jeszcze nie zajęta
W każdej z tych 6 kombinacji drugą literę możemy wybrać również na 6 różnych sposobów. Na przykład dla Cxxx otrzymamy:
CAxx, CBxx, CCxx, CDxx, CExx, CFxx
Zatem ze względu na dwie pierwsze litery kodu otrzymujemy
BFAx, BFBx, BFCx, BFDx, BFEx, BFFx
Ze względu na 3 pierwsze litery kodu otrzymamy
Nie jest to zbiór specjalnie duży dla współczesnego komputera z procesorem Pentium –
możemy zastosować metodę naiwną, która polega na tym, iż tworzymy zbiór Z wszystkich możliwych słówek kodowych. Następnie w pętli wykonującej się
maksymalnie 6 razy losujemy ze zbioru Z jedno słowo kodowe słamacz, usuwamy je ze zbioru
Z i prezentujemy człowiekowi. W odpowiedzi człowiek zwraca
nam kod klucza sklucz zbudowany z liter x i
o wg zasad
gry
Pomimo swojej prostoty algorytm ten zupełnie dobrze radzi sobie z odgadywaniem kodów kodera w 6 ruchach. Oczywiście, z uwagi na losowy charakter typowania przez komputer swoich ruchów, istnieje mała szansa, iż komputer przegra wybierając nieefektywne posunięcia (tzn. takie, które eliminują ze zbioru Z zbyt małą liczbę kodów).
Zanim przystąpimy do tworzenia algorytmu, musimy rozwiązać kilka problemów.
Pierwszym z nich jest reprezentacja zbioru Z, który musi posiadać właściwość
usuwania elementów. Użyjemy do tego celu tablicy Z oraz dodatkowej zmiennej
zn, która będzie przechowywała liczbę elementów pozostałych w tablicy.
Aby system ten był efektywny, pozostałe elementy muszą być umieszczone w kolejnych komórkach
Teraz musimy określić operację usuwania elementu z tej struktury danych. Z tablicy nie można tak po prostu usunąć komórki. Dlatego przez usunięcie będziemy rozumieli zmniejszenie zawartości zn oraz przesunięcie o jedną pozycję wstecz zawartości wszystkich komórek za komórką, z której usuwamy dane.
Przykład:
Z[] przed operacją, zn = 10 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
---|---|---|---|---|---|---|---|---|---|---|
Usuwanie |
|
|
|
|
|
X |
← |
← |
← |
← |
Z[] po operacji, zn = 9 |
0 |
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
9 |
Widzimy, iż fizycznie tablica dalej posiada 10 elementów. Ponieważ jednak zn zostało zmniejszone, to pod uwagę bierzemy teraz tylko 9 pierwszych elementów tablicy. Element 5 został nadpisany i nie występuje już w tym obszarze tablicy – usunęliśmy go.
Operacja usuwania jest nam potrzebna po wylosowaniu kodu – aby nie był
ponownie losowany. Ze zbioru Z musimy dodatkowo usuwać kody, które nie zgadzają
się z otrzymanym kluczem. Zastosujemy tutaj nieco inną operację usuwania ze względu na liniową złożoność obliczeniową (zastosowanie
zwykłej operacji usuwania prowadziłoby do złożoności kwadratowej).
Polega ona na tym, iż ustawiamy dwa indeksy
Przykład:
Lp. | Operacja | Opis |
1. |
0 1 2 3 4 5 6 7 8 9 i1 i2 |
Ustawiamy oba indeksy na pierwszy
element zbioru. Za pomocą indeksu i1 przeglądamy kolejne elementy zbioru. |
2. |
0 1 2 3 4 5 6 7 8 9 i1 i2 |
0 kopiujemy (na
siebie, co nie jest szkodliwe). Oba indeksy są zwiększane o 1. |
3. |
0 1 2 3 4 5 6 7 8 9 i1 i2 |
1 kopiujemy, zwiększamy o 1 oba indeksy. |
4. |
0 1 2 3 4 5 6 7 8 9 i1 i2 |
2 kopiujemy, zwiększamy o 1 oba indeksy. |
5. |
0 1 2 3 4 5 6 7 8 9 i1 i2 |
3 pomijamy. Indeks i2 nie jest zwiększany. |
6. |
0 1 2 4 4 5 6 7 8 9 ↑ i1 i2 |
4 kopiujemy na byłe miejsce
3, zwiększamy o 1 oba indeksy. |
7. |
0 1 2 4 5 5 6 7 8 9 ↑ i1 i2 |
5 kopiujemy na byłe miejsce
4, zwiększamy o 1 oba indeksy. |
8. |
0 1 2 4 5 5 6 7 8 9 ↑ i1 i2 |
6 pomijamy |
9. |
0 1 2 4 5 5 6 7 8 9 ↑ i1 i2 |
7 pomijamy |
10. |
0 1 2 4 5 5 6 7 8 9 ↑ i1 i2 |
8 pomijamy |
11. |
0 1 2 4 5 9 6 7 8 9 ↑ i1 i2 |
9 kopiujemy na byłe miejsce
5, zwiększamy i2. |
12 |
0 1 2 4 5 9 6 7 8 9 ↑ i1 i2 |
Koniec. W zbiorze pozostało i2 elementów, wśród których nie ma już elementów pominiętych. |
Kolejnym problemem jest sposób wygenerowania 1296 łańcuchów kodowych. Zadanie
można rozwiązać na wiele sposobów. Jednym z nich jest potraktowanie każdego kodu
jako 4 cyfrowej liczby szóstkowej. W systemie szóstkowym jest 6 cyfr
Przykład:
Obliczymy wg tej metody wyraz o numerze 545:
545:6 = 90 i reszta 5 → F 90:6 = 15 i reszta 0 → A 15:6 = 2 i reszta 3 → D 2:6 = 0 i reszta 2 → C
Otrzymaliśmy wyraz
K01: Utwórz kody w tablicy Z ; tablicę Z wypełniamy 1296 kodami 4 znakowymi K02: zn ← 1296 ; początkowa liczba kodów w tablicy K03: Dla runda = 1, 2, …, 6: ; rozpoczynamy rozgrywkę wykonuj kroki K04…K18 K04: Jeśli zn = 0, ; jeśli zbiór Z jest pusty, kończymy – błędne dane człowieka to idź do kroku K19 K05: i2 ← losowa(zn) ; losujemy słówko kodowe K06: słamacz ← Z[i2] ; umieszczamy je w łańcuchu słamacz K07: Usuń kod Z[i2] ze zbioru Z K08: zn ← zn-1 ; usuwamy wylosowane słowo i zmniejszamy zn o 1 K09: Pisz runda, słamacz ; wyświetlamy słowo kodowe dla człowieka K10: Czytaj sklucz ; odczytujemy klucz K11: Jeśli sklucz = "xxxx", ; słowo odgadnięte? Jeśli tak, to kończymy to idź do kroku K19 K12: i2 ← 0 K13: Dla i1 = 0, 1, …, zn-1: ; przeglądamy zbiór Z i usuwamy z niego elementy wykonuj kroki K14…K17 ; nie pasujące do otrzymanego klucza K14: Oblicz sklucz2 dla słamacz i Z[i1] ; algorytm obliczania klucza ; podaliśmy w poprzednim rozdziale K15: Jeśli sklucz2 ≠ sklucz, ; słowa kodowe nie pasujące do klucza pomijamy to następny obieg pętli K13 K16: Z[i2] ← Z[i1] ; słowa pasujące do klucza kopiujemy pod i2 K17: i2 ← i2+1 K18: zn ← i2 ; korygujemy liczbę elementów zbioru Z K19: Jeśli sklucz = "xxxx", to zakończ
K20: Dla i1 = 0, 1, …, zn-1: ; wypisujemy pozostałe w zbiorze Z kody wykonuj: pisz Z[i1] K21: Zakończ
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
Program prowadzi rozgrywkę
|
Pascal// MasterMind-komputer <-> człowiek // Data: 17.08.2008 // (C)2020 mgr Jerzy Wałaszek //--------------------------------- program prg; var Z : array [0..1295] of string; skoder, slamacz, sklucz, sklucz2, sl : string; zn, i, i1, i2, j, runda : integer; begin randomize; // tworzymy zbiór Z for i := 0 to 1295 do begin Z[i] := ''; i2 := i; for j := 1 to 4 do begin Z[i] := Z[i]+chr(65+(i2 mod 6)); i2 := i2 div 6; end; end; zn := 1296; // liczba kodów w Z // rozgrywka for runda := 1 to 6 do if zn > 0 then begin i2 := random(zn); slamacz := Z[i2]; // usuwamy wylosowane słowo ze zbioru Z for i := i2+1 to zn-1 do Z[i-1] := Z[i]; dec(zn); // wylosowane słowo prezentujemy człowiekowi write('Runda ', runda, ' : ', slamacz, zn:5, ' : '); // odczytujemy kod klucza readln(sklucz); // analizujemy dane if sklucz = 'xxxx' then break; // ze zbioru Z wyrzucamy nie pasujące kody i2 := 0; for i1 := 0 to zn-1 do begin skoder := Z[i1]; sl := slamacz; sklucz2 := ''; for i := 1 to 4 do if skoder[i] = slamacz[i] then begin sklucz2 := sklucz2+'x'; skoder[i] := '#'; // wartownik w1 sl[i] := '$'; // wartownik w2 end; for i := 1 to 4 do if skoder[i] <> '#' then for j := 1 to 4 do if skoder[i] = sl[j] then begin sklucz2 := sklucz2+'o'; sl[j] := '$'; // wartownik w2 break; end; if sklucz = sklucz2 then begin Z[i2] := Z[i1]; inc(i2); end; end; zn := i2; end else break; // wyświetlamy pozostałe kody w Z writeln; if sklucz <> 'xxxx' then begin for i := 1 to zn do write(Z[i-1], ' '); writeln; writeln; end; end. |
// MasterMind-komputer <-> człowiek // Data: 17.08.2008 // (C)2020 mgr Jerzy Wałaszek //--------------------------------- #include <iostream> #include <iomanip> #include <string> #include <cstdlib> #include <ctime> using namespace std; int main() { string Z[1296], skoder, slamacz, sklucz, sklucz2, sl; int zn, i, i1, i2, j, runda; srand(time(NULL)); // tworzymy zbiór Z for(i = 0; i < 1296; i++) { Z[i] = ""; i2 = i; for(j = 0; j < 4; j++) { Z[i] += char(65+(i2%6)); i2 /= 6; } } zn = 1296; // liczba kodów w Z // rozgrywka for(runda = 1; runda <= 6; runda++) if(zn > 0) { i2 = rand()%zn; slamacz = Z[i2]; // usuwamy wylosowane słowo ze zbioru Z for(i = i2+1; i < zn; i++) Z[i-1] = Z[i]; zn--; // wylosowane słowo prezentujemy człowiekowi cout << "Runda " << runda << ": " << slamacz << setw(5) << zn << ": "; // odczytujemy kod klucza getline(cin, sklucz); // analizujemy dane if(sklucz == "xxxx") break; // ze zbioru Z wyrzucamy nie pasujące kody for(i2 = i1 = 0; i1 < zn; i1++) { skoder = Z[i1]; sl = slamacz; sklucz2 = ""; for(i = 0; i < 4; i++) if(skoder[i] == slamacz[i]) { sklucz2 += 'x'; skoder[i] = '#'; // wartownik w1 sl[i] = '$'; // wartownik w2 } for(i = 0; i < 4; i++) if(skoder[i] != '#') for(j = 0; j < 4; j++) if(skoder[i] == sl[j]) { sklucz2 += 'o'; sl[j] = '$'; // wartownik w2 break; } if(sklucz == sklucz2) Z[i2++] = Z[i1]; } zn = i2; } else break; // wyświetlamy pozostałe kody w Z cout << endl; if(sklucz != "xxxx") { for(i = 1; i <= zn; i++) cout << Z[i-1] << " "; cout << endl << endl; } return 0; } |
Basic' MasterMind-komputer <-> człowiek ' Data: 17.08.2008 ' (C)2020 mgr Jerzy Wałaszek '--------------------------------- Dim As String Z(1295), skoder, slamacz, sklucz, sklucz2, sl Dim As Integer zn, i, i1, i2, j, runda Randomize ' tworzymy zbiór Z For i = 0 To 1295 Z(i) = "": i2 = i For j = 1 To 4 Z(i) += Chr(65+(i2 Mod 6)) i2 \= 6 Next Next zn = 1296 ' liczba kodów w Z ' rozgrywka For runda = 1 To 6 If zn > 0 Then i2 = Cint(Rnd*(zn-1)) slamacz = Z(i2) ' usuwamy wylosowane słowo ze zbioru Z For i = i2+1 To zn-1 Z(i-1) = Z(i) Next zn -= 1 ' wylosowane słowo prezentujemy człowiekowi Print Using "Runda # : &##### : ";runda;slamacz;zn; ' odczytujemy kod klucza Line Input sklucz ' analizujemy dane If sklucz = "xxxx" Then Exit For ' ze zbioru Z wyrzucamy nie pasujące kody i2 = 0 For i1 = 0 To zn-1 skoder = Z(i1) sl = slamacz sklucz2 = "" For i = 1 To 4 If Mid(skoder, i, 1) = Mid(slamacz, i, 1) Then sklucz2 += "x" Mid(skoder, i, 1) = "#" ' wartownik w1 Mid(sl, i, 1) = "$" ' wartownik w2 End If Next For i = 1 To 4 If Mid(skoder, i, 1) <> "#" Then For j = 1 To 4 If Mid(skoder, i, 1) = Mid(sl, j, 1) Then sklucz2 += "o" Mid(sl, j, 1) = "$" ' wartownik w2 Exit For End If Next End If Next If sklucz = sklucz2 Then Z(i2) = Z(i1) i2 += 1 End If zn = i2 Next Else Exit For End If Next ' wyświetlamy pozostałe kody w Z Print If sklucz <> "xxxx" Then For i = 1 To zn Print Z(i-1);" "; Next Print: Print End If End |
Python
(dodatek)# MasterMind-komputer <-> człowiek # Data: 24.03.2024 # (C)2024 mgr Jerzy Wałaszek #--------------------------------- import random sklucz = '' # tworzymy zbiór Z z = [] for i in range(1296): z.append("") i2 = i for j in range(4): z[i] += chr(65+(i2%6)) i2 //= 6 zn = 1296 # liczba kodów w Z # rozgrywka for runda in range(1, 7): if zn > 0: i2 = random.randrange(zn) slamacz = z[i2][:] # usuwamy wylosowane słowo ze zbioru Z for i in range(i2+1, zn): z[i-1] = z[i][:] zn -= 1 # wylosowane słowo prezentujemy człowiekowi print("Runda", runda, ":", slamacz, "%5d" % zn, ":", end=" ") # odczytujemy kod klucza sklucz = input() # analizujemy dane if sklucz == "xxxx": break # ze zbioru Z wyrzucamy nie pasujące kody i2 = 0 for i1 in range(zn): skoder = z[i1][:] sl = slamacz[:] sklucz2 = "" for i in range(4): if skoder[i] == slamacz[i]: sklucz2 += 'x' skoder = skoder[:i]+'#'+skoder[i+1:] sl = sl[:i]+'$'+sl[i+1:] for i in range(4): if skoder[i] != '#': for j in range(4): if skoder[i] == sl[j]: sklucz2 += 'o' sl = sl[:j]+'$'+sl[j+1:] break if sklucz == sklucz2: z[i2] = z[i1][:] i2 += 1 zn = i2 else: break # wyświetlamy pozostałe kody w Z print() if sklucz != "xxxx": for i in range(1, zn+1): print(z[i-1], end=" ") print() print() |
Wynik: |
Runda 1 : CBFF 1295 : o Runda 2 : EAAB 275 : xxo Runda 3 : EABE 14 : xx Runda 4 : EACA 2 : xo Runda 5 : AABB 0 : xxxx |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.