一日一Shader·八皇后【SS_10】

今天突然想不起来回溯怎么写了,于是开始研究经典的回溯题——八皇后问题。但是在看到回溯的原理的时候,脑子有点转不过弯来了。于是决定将回溯过程绘制出来。

Shader "MyShader/SS_10"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color",Color)=(1,1,1,1)
        _Size("Size",float)=0.03
        _Pos0("Pos0",Vector)=(-1,-1,1,1)
        _Pos1("Pos1",Vector)=(-1,-1,1,1)
        _Pos2("Pos2",Vector)=(-1,-1,1,1)
        _Pos3("Pos3",Vector)=(-1,-1,1,1)
        _Pos4("Pos4",Vector)=(-1,-1,1,1)
        _Pos5("Pos5",Vector)=(-1,-1,1,1)
        _Pos6("Pos6",Vector)=(-1,-1,1,1)
        _Pos7("Pos7",Vector)=(-1,-1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {                    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;                
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Size;               
            float4 _Pos0;
            float4 _Pos1;
            float4 _Pos2;
            float4 _Pos3;
            float4 _Pos4;
            float4 _Pos5;
            float4 _Pos6;
            float4 _Pos7;
            float4 _Color;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {                
                fixed4 col = tex2D(_MainTex, i.uv);
                if((pow((i.uv.x-(_Pos0.x)/2-0.25),2)+pow((i.uv.y-(_Pos0.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos1.x)/2-0.25),2)+pow((i.uv.y-(_Pos1.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos2.x)/2-0.25),2)+pow((i.uv.y-(_Pos2.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos3.x)/2-0.25),2)+pow((i.uv.y-(_Pos3.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos4.x)/2-0.25),2)+pow((i.uv.y-(_Pos4.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos5.x)/2-0.25),2)+pow((i.uv.y-(_Pos5.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos6.x)/2-0.25),2)+pow((i.uv.y-(_Pos6.y)/2-0.25),2))<_Size){
                       col=_Color;
                }else  if((pow((i.uv.x-(_Pos7.x)/2-0.25),2)+pow((i.uv.y-(_Pos7.y)/2-0.25),2))<_Size){
                       col=_Color;
                }
                return col;
            }
            ENDCG
        }             
    }
}

绘制8个棋子就要声明8个参数,感觉太笨了,或许用ComputeShader改编一下会美观一点。不过,今天的主题不是这个,还是先把回溯算法搞清楚。

需要编写控制脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SS_10 : MonoBehaviour
{
    private Material mat;
    public float cd = 0.3f;
    public Material Mat
    {
        get
        {
            if (mat == null)
            {
                mat = GetComponent<MeshRenderer>().material;
            }
            return mat;
        }
        set
        {
            mat = value;
        }
    }
    private void Start()
    {
        Draw();
    }
    void Draw()
    {
        Clear();
        int n = 8;
        var x = new int[n + 1];
        for (int i = 0; i < n; i++)
        {
            x[i] = -1;
        }
        StartCoroutine(IE_DrawProcess(n, x));
        //StartCoroutine(IE_ShowQueens(GetQueen(n, x)));
    }
    //展示所有满足条件的结果
    IEnumerator IE_ShowQueens(List<Vector2Int[]> _list)
    {
        for (var i = 0; i < _list.Count; i++)
        {
            for (var j = 0; j < _list[i].Length; j++)
            {
                SetMatValue(j, _list[i][j]);
                yield return new WaitForSeconds(0.1f);
            }
            yield return new WaitForSeconds(cd);
            Clear();
            yield return new WaitForSeconds(cd);
        }
    }
    //修改材质球,以显示棋子位置
    void SetMatValue(int _index, Vector2Int _v)
    {
        Mat.SetVector("_Pos" + _index, new Vector4(_v.x, _v.y, 0, 0));
    }
    //清空所有棋子
    void Clear()
    {
        for (var i = 0; i < 8; i++)
        {
            SetMatValue(i, new Vector2Int(-1, -1));
        }
    }
    //获取所有满足条件的结果
    List<Vector2Int[]> GetQueen(int n, int[] x)
    {
        List<Vector2Int[]> queens = new List<Vector2Int[]>();
        int k = 0;
        while (k >= 0)
        {
            x[k]++;
            while (x[k] < n && !CheckQueen(k, x))
            {
                x[k]++;
            }
            if (x[k] < n && k == n - 1)
            {
                var ans = new Vector2Int[8];
                for (int i = 0; i < n; i++)
                {
                    ans[i] = new Vector2Int(i, x[i]);
                }
                queens.Add(ans);
            }
            else if (x[k] < n && k < n - 1)
            {
                k++;
            }
            else
            {
                x[k] = -1;
                k--;
            }
        }
        return queens;
    }
    //查看k皇后是否满足约束条件
    bool CheckQueen(int k, int[] x)
    {
        for (int i = 0; i < k; i++)
            if (x[i] == x[k] || Mathf.Abs(x[i] - x[k]) == Mathf.Abs(i - k))  //满足在同一行或同一条斜线(斜率相同)
                return false;
        return true;
    }
    //展示回溯的过程
    IEnumerator IE_DrawProcess(int n, int[] x)
    {
        int k = 0;//从 皇后0 开始放            
        while (k >= 0)
        {
            //摆放第k列的皇后, 摆在第x[k]+1行(x[k]初始为-1)
            x[k]++;
            //对皇后k进行检测,直到不发生冲突或x[k]越界 ;检测到第7行时,下一次检测第8行后就会越界
            while (x[k] < n && !CheckQueen(k, x))
            {
                x[k]++;//检测下一行
                yield return new WaitForSeconds(0.01f);
            }
            SetMatValue(k, new Vector2Int(k, x[k]));
            //8个棋子都放上去了的时候,如果x[k]==n说明放到了棋盘外;反之结果是正确的
            if (x[k] < n && k == n - 1)
            {
                mat.SetColor("_Color", Color.green);
                yield return new WaitForSeconds(2);
                mat.SetColor("_Color", Color.white);
            }
            else if (x[k] < n && k < n - 1)//若皇后还没有摆放完,就摆放下一个皇后k =  k+1
            {
                k++;
            }
            //发生了越界,要进行回溯【核心】
            else
            {
                x[k] = -1;//将该列清空
                k--;//回到上一列,它会增加一行后继续检测【比如第k-1列的当前行是3,从k回溯到k-1后,会从第4行开始检测,当第8行也检测失败时,继续向上回溯】
            }
            yield return new WaitForSeconds(cd);
        }
    }
}

先看看回溯过程:

用shader绘制出来了整个回溯过程,应该不会再忘了吧。

另外还能看看所有满足条件的结果:

猜你喜欢

转载自blog.csdn.net/yzy1987523/article/details/107005995