1. 程式人生 > >MATLAB—一字棋(極大極小搜尋)

MATLAB—一字棋(極大極小搜尋)

init.m

%初始化棋盤狀態
function cur=init()
    cur=rand(3,3);  %儲存當前棋盤的狀態
    %計算機為先手時的初值,即均為0
    for i=1:3
        for j=1:3
            cur(i,j)=0;
        end
    end
end

checkWin.m

%檢查是否有一方贏棋(0:沒有任何一方贏;1:計算機贏;-1:人贏)
function flag = checkWin(cur)
    %該方法沒有判斷平局
    for i=1:3        
                      %判斷一行
        if cur(i,1)==1&&cur(i,2)==1&&cur(i,3)==1     %是否計算機贏
            flag=1;
            return;
        end
        if cur(i,1)==-1&&cur(i,2)==-1&&cur(i,3)==-1  %是否人贏
            flag=-1;
            return;
        end
                      %判斷一列
        if cur(1,i)==1&&cur(2,i)==1&&cur(3,i)==1     %是否計算機贏
            flag=1;
            return;
        end
        if cur(1,i)==-1&&cur(2,i)==-1&&cur(3,i)==-1  %是否人贏
            flag=-1;
            return;
        end
    end
    
    %判斷兩個對角線
    if (cur(1,1)==1&&cur(2,2)==1&&cur(3,3)==1)||(cur(3,1)==1&&cur(2,2)==1&&cur(1,3)==1)  %是否計算機贏
        flag=1;
        return;
    end
    if((cur(1,1)==-1&&cur(2,2)==-1&&cur(3,3)==-1)||(cur(3,1)==-1&&cur(2,2)==-1&&cur(1,3)==-1))%是否人贏
        flag=-1;
        return;
    end
    
    %沒有任何一方贏
    flag=0;  
    return;
end


value.m

%評估當前棋盤狀態的值(同時可以用p或q判斷是否平局)
function pq= value(cur)
p=0;
q=0;
isWin=checkWin(cur);
tmpQP=rand(3,3);   %表示棋盤資料的臨時陣列,其中的元素0表示該格為空

if(isWin==-1)      %如果使用者玩家贏了,置棋盤估計值為負無窮
    pq=-10000;
    return;
end
if(isWin==1)       %如果計算機贏了,置棋盤估計值為無窮
    pq=10000;
    return;  
end

%計算機一方(MAX)
for i=1:3
    %將棋盤中的空格填滿自己的棋子,既將棋盤陣列中的0變為1
    for j=1:3
        if cur(i,j)==0
            tmpQP(i,j)=1;
        else
            tmpQP(i,j)=cur(i,j);
        end
    end
end
for i=1:3         %計算共有多少連成3個1的行
    if tmpQP(i,1)+tmpQP(i,2)+tmpQP(i,3)==3
        p=p+1;
    end
end
for i=1:3         %計算共有多少連成3個1的列
    if tmpQP(1,i)+tmpQP(2,i)+tmpQP(3,i)==3
        p=p+1;
    end
end
if tmpQP(1,1)+tmpQP(2,2)+tmpQP(3,3)==3
    p=p+1;        %計算共有多少連成3個1的對角線
end
if tmpQP(3,1)+tmpQP(2,2)+tmpQP(1,3)==3
    p=p+1;
end

%人一方(MIN)
for i=1:3
    %將棋盤中的空格填滿自己的棋子,既將棋盤陣列中的0變為-1
    for j=1:3
        if cur(i,j)==0
            tmpQP(i,j)=-1;
        else
            tmpQP(i,j)=cur(i,j);
        end
    end
end
for i=1:3        %計算共有多少連成3個-1的行
    if tmpQP(i,1)+tmpQP(i,2)+tmpQP(i,3)==-3
        q=q+1;
    end
end
for i=1:3        %計算共有多少連成3個-1的列
    if tmpQP(1,i)+tmpQP(2,i)+tmpQP(3,i)==-3
        q=q+1;
    end
end
if tmpQP(1,1)+tmpQP(2,2)+tmpQP(3,3)==-3
    q=q+1;       %計算共有多少連成3個-1的對角線
end
if tmpQP(3,1)+tmpQP(2,2)+tmpQP(1,3)==-3
    q=q+1;
end

%返回評估出的棋盤狀態的值
pq=p-q;  
end

tuozhan.m

function [tail,open] = tuozhan(g,tail,open,index)
cur=open{1,index}.S;
k=0;
%對當前狀態進行拓展
for i=1:3
    for j=1:3
        flag=1;         %標誌拓展後的結點是否與之前拓展的結點同構
        
        if cur(i,j)==0
            k=k+1;
            tmpcur=cur;
            tail=tail+1;
            %判斷當前結點是MAX還是MIN走的
            if g/2~=1            %根據拓展深度的不同,值有所變化,此程式只試探兩步
                tmpcur(i,j)=1;   %若是MIN走的,下一步MAX走
            else
                tmpcur(i,j)=-1;  %若是MAX走的,下一步MIN走
            end
            %判斷拓展後的結點是否和之前的結點同構,若同構flag=0,否則flag=1
            for k=index:tail-1
                if open{1,k}.S==rot90(tmpcur,1)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(tmpcur,2)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(tmpcur,3)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==fliplr(tmpcur)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==flipud(tmpcur)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                
                if open{1,k}.S==rot90(flipud(tmpcur),1)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(flipud(tmpcur),2)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(flipud(tmpcur),3)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(fliplr(tmpcur),1)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(fliplr(tmpcur),2)
                    flag=0;
                    tail=tail-1;
                    break;
                end
                if open{1,k}.S==rot90(fliplr(tmpcur),3)
                    flag=0;
                    tail=tail-1;
                    break;
                end
            end
            %如果flag不為0,說明沒有同構,可以拓展,加入open表裡
            if flag~=0
                open{1,tail}.g=g;
                open{1,tail}.v=value(tmpcur);
                open{1,tail}.S=tmpcur;   
                open{1,tail}.fa=cur;
            end 
        end 
        
    end
end

end


chushi.m

function table=chushi(cur)
    table=cell(1);
    table{1,1}.g=0;
    table{1,1}.v=value(cur);
    table{1,1}.S=cur;  %當前節點的狀態
    table{1,1}.fa=[]; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態

yunxing.m

%第一階段 A=[0 0 0;0 0 0;0 0 0]
%第二階段 A=[0 -1 0;0 1 0;0 0 0]
%第三階段 A=[0 -1 -1;0 1 0;1 0 0]
function jieguo = yunxing(cur)
    
    close=cell(1);
    open=chushi(cur);
    index=1;            %記錄拓展結點的下標,為tuozhan.m方便
    tail=1;
    g=1;
    c=1;
    
    while 1
        if g>2          %若深度大於2,則停止
            break;
        end
        [tail,open]=tuozhan(g,tail,open,index);
        close{1,c}=open{1,index};   %將已擴充套件的節點放入CLOSED8表中
        c=c+1;
        index=index+1; 
        if index<=tail
            g=open{1,index}.g;
            g=g+1;         
        else     %當只剩程式方最後一步時
            break;
        end
    end
    
    %端結點靜態估值和倒推值計算
    i=c-1;             %查close表
    while i>0
        max=-1000000;
        min=1000000;
        j=tail;         %查open表
        while j>1
            if open{1,j}.fa==close{1,i}.S
                if close{1,i}.g==1     %因只拓展兩層,所以可以直接寫1和0,若拓展深度大於2,則做出相應的改變
                  if open{1,j}.v<min
                     min=open{1,j}.v;
                  end
                end
                if close{1,i}.g==0  
                  if open{1,j}.v>max
                     max=open{1,j}.v;
                  end
                end
            end
            j=j-1;
        end
        
        for k=1:tail                  %將改過的v寫回open表
            if open{1,k}.S==close{1,i}.S
                if close{1,i}.g==1
                    open{1,k}.v=min;
                end
                if close{1,i}.g==0
                    open{1,k}.v=max;
                end
            end   
        end
        if close{1,i}.g==1
            close{1,i}.v=min;%更新close表中的值
        end
        if close{1,i}.g==0
            close{1,i}.v=max;%更新close表中的值
        end
        i=i-1;
    end
    
    %輸出結果
    for i=1:tail
        if open{1,i}.g==1
           if open{1,i}.v==open{1,1}.v
               jieguo=open{1,i}.S;
               disp('程式方:');
               disp(open{1,i}.S);   %查詢open表,看取的哪一個MIN中的MAX值,然後輸出此矩陣
               break;
           end
        end
    end
    
end


operation.m

function []=operation()
input('*****程式方先手*****');
step=0;              %記錄走的步數,以判斷是否最後的平局
cur=init();          %當程式方為先手時
    while 1

        jieguo=yunxing(cur);
        step=step+1;
        if checkWin(jieguo)==1
           disp('計算機贏,您輸了!'); 
           break;
        end
        if checkWin(jieguo)==-1
            disp('恭喜,您贏了!');
            break;
        end
        if checkWin(jieguo)==0
            if step==9
                disp('平局');
                break;
            end
        end
        disp('請輸入位置:');
        matri = input('[a b] = ');
        a = matri(1);    %橫座標
        b = matri(2);    %縱座標
        while jieguo(a,b)~=0
            disp('輸入有誤!');
            disp('請重新輸入位置:');
            matri = input('[a b] = ');
            a = matri(1);    %橫座標
            b = matri(2);    %縱座標
        end

        jieguo(a,b)=-1;
        disp('我方:');
        disp(jieguo);
        step=step+1;
        cur=jieguo;     
    end
    
end

執行結果: