Nell’articolo precedente ho mostrato un package PL/SQL che funge da “codemaker” e consente di giocare a Master Mind contro il db Oracle.
A questo punto mi sono anche posto il problema di scrivere un programma che potesse automaticamente giocare una partita nel modo migliore possibile.
Ho scritto quindi il package mmp (scaricatelo qui).
Ecco la spec:
create or replace package mmp is function goAlone return number; end; /
Molto semplice, la funzione goAlone avvia una nuova partita e la gioca, restituendo il numero di tentativi che sono stati necessari a trovare la soluzione.
Vediamo un esempio:
SQL> select mmp.goAlone from dual; GOALONE ---------- 6 ####################################### 01 C6 C2 C1 C5:BW ####################################### TRY AGAIN. After this guess there are still 660 possible solutions ####################################### 01 C6 C2 C1 C5:BW 02 C3 C2 C8 C1:W ####################################### TRY AGAIN. After this guess there are still 147 possible solutions ####################################### 01 C6 C2 C1 C5:BW 02 C3 C2 C8 C1:W 03 C5 C1 C4 C5:BW ####################################### TRY AGAIN. After this guess there are still 23 possible solutions ####################################### 01 C6 C2 C1 C5:BW 02 C3 C2 C8 C1:W 03 C5 C1 C4 C5:BW 04 C4 C6 C3 C5:B ####################################### TRY AGAIN. After this guess there are still 6 possible solutions ####################################### 01 C6 C2 C1 C5:BW 02 C3 C2 C8 C1:W 03 C5 C1 C4 C5:BW 04 C4 C6 C3 C5:B 05 C7 C5 C2 C5:BBW ####################################### TRY AGAIN. After this guess there are still 1 possible solutions ####################################### 01 C6 C2 C1 C5:BW 02 C3 C2 C8 C1:W 03 C5 C1 C4 C5:BW 04 C4 C6 C3 C5:B 05 C7 C5 C2 C5:BBW 06 C2 C5 C5 C5:BBBB ####################################### YOU GOT IT! WELL DONE!!! C2 C5 C5 C5
Qualche dettaglio sull’algoritmo:
All’inizio il package memorizza in un array associativo tutte le 4096 possibili combinazioni in ordine casuale.
procedure fillSolTab is begin if solTab.count > 0 then solTab.delete(solTab.first,solTab.last); end if; with t as ( select mm.c1 col from dual union all select mm.c2 from dual union all select mm.c3 from dual union all select mm.c4 from dual union all select mm.c5 from dual union all select mm.c6 from dual union all select mm.c7 from dual union all select mm.c8 from dual) select t1.col, t2.col, t3.col, t4.col bulk collect into solTab from t t1, t t2, t t3, t t4 order by dbms_random.value; end;
Poi prende la prima e la prova.
function nextTry return number is res mm.t_res; sol t_sol := solTab(solTab.first); begin res := mm.tryThis(sol.s1,sol.s2,sol.s3,sol.s4); if res.game_over = 0 then cleanSolTab(res.r1||res.r2||res.r3||res.r4); end if; return res.game_over; end;
Ovviamente ottiene dal package mm un feedback che viene utilizzato per eliminare dalla tabella di tutte le combinazioni quelle che non possono essere più valide.
procedure cleanSolTab(p_res in varchar2) is i number; currSol t_sol; markForDelete number; begin i := solTab.first; currSol := solTab(i); solTab.delete(i); i := solTab.first; loop if nvl(checkSol(solTab(i),currSol),'x') != nvl(p_res,'x') then markForDelete := i; else markForDelete := 0; end if; exit when i=solTab.last; i := solTab.next(i); if markForDelete>0 then solTab.delete(markForDelete); end if; end loop; if markForDelete>0 then solTab.delete(markForDelete); end if; dbms_output.put_line('After this guess there are still '||solTab.count||' possible solutions'); end;
Dopodiché continua a provare in questo modo finché non trova la combinazione giusta.
function goAlone return number is num number:=0; begin startGame; loop num := num+1; exit when nextTry != 0; end loop; return num; end;
Se si gioca un numero molto alto di partite (non vi dimenticate di spegnere il serverout!!) si vede che il mio metodo ottiene la soluzione con un numero medio di circa 5,5 tentativi:
SQL> select min(g), max(g), avg(g), stddev(g) 2 from (select mmp.goalone g from dual connect by level<=100); MIN(G) MAX(G) AVG(G) STDDEV(G) ---------- ---------- ---------- ---------- 3 8 5.47 .968754277
Se qualcuno trova un algoritmo che consente di ottenere una media più bassa mi faccia sapere…
Massimo
Lascia un commento