FolkeLogo

Welcome to my AI section.


Here are some projects related to AI I have been working on:

TicTacToe:



  • Description: Simple AI to compete against in a classic game of Tic Tac Toe. The AI are using the Minimax algorithm. It is unbeatable, unless you settle for a tie. 


  • Made in: Unity.


  • Time: 2 Days.






          		
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TicTac : MonoBehaviour
{
    public GameManager gameManager;
    public GameObject RestartButton;
    public Button Player;
    public bool Clickable = true;
    public int Index = 0;
    public Text Current;
    public Text WinText;
    public string[] SetCurrentTurn = { "X", "O" };
    static private bool PlayerTurn = true;
    bool AiTurnTime = true;
    public int Value;
    public int Row;
    public int Col;

    
    public void OnClickediClick()
    {
        if (Clickable)
        {
            if (PlayerTurn)
            {
                Current.text = "X";
                PlayerTurn = false;
                Index = 1;
            }
            else
            {
                Current.text = "O";
                PlayerTurn = true;
                Index = 2;
            }
        }
        if (gameManager.GetComponent<GameManager>().CheckifVictory(Index) != 0)
        {
            if (Index == 1)
                WinText.text = "VICTORY TO:X";
            else
                WinText.text = "VICTORY TO:O";

            RestartButton.SetActive(true);
            PlayerTurn = true;
        }
        Clickable = false;
        if (!PlayerTurn)
        {
            gameManager.gameObject.GetComponent<AIScript>().AITurn(gameManager.AllButtons);
        }
    }
    public void Restart()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
    public void AiTurn()
    {
        gameManager.gameObject.GetComponent<AIScript>().AITurn(gameManager.AllButtons);
    }
}
        
	
          		
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AIScript : MonoBehaviour
{

    public GameManager GameManager;
    public void Start()
    {
        GameManager = gameObject.GetComponent<GameManager>();
    }
    public void AITurn(List<Button> Board)
    {
        int bestMove = 1000;
        int BestButtonIndex = 0;
        for (int i = 0; i < Board.Count; i++)
        {
           if (Board[i].GetComponent<TicTac>().Clickable)
            {
                TicTac currentPlayer = Board[i].GetComponent<TicTac>();
                currentPlayer.Index = 2;
                currentPlayer.Clickable = false;
                
                int MoveValue = Minimax(Board, 0, true);
                
                currentPlayer.Index = 0;
                currentPlayer.Clickable = true;
                if (MoveValue < bestMove)
                {
                    BestButtonIndex = i;
                    bestMove = MoveValue;
                }
            }
        }
        Board[BestButtonIndex].GetComponent<Button>().onClick.Invoke();

    }
    bool canPlay(List<Button> Board)
    {
        for (int i = 0; i < Board.Count; i++)
        {
            if (Board[i].GetComponent<TicTac>().Clickable)
                return true;
        }
        return false;
    }

    public int Minimax(List<Button> Board, int depth, bool minimax)
    {

        int score = GameManager.CheckifVictory(1);
        if (score == 0)
        {
            score = GameManager.CheckifVictory(2);
        }

        if (score != 0)
        {
            return score;
        }
        if (canPlay(Board) == false)
            return 0;

        if (minimax)
        {
            int best = -1000;

            for (int i = 0; i < Board.Count; i++)
            {
                if (Board[i].GetComponent<TicTac>().Clickable)
                {
                    TicTac currentPlayer = Board[i].GetComponent<TicTac>();
                    currentPlayer.Index = 1;
                    currentPlayer.Clickable = false;

                    int Tempbest = Mathf.Max(best, Minimax(Board, depth + 1, !minimax));
                    best = Mathf.Max(best, Tempbest);
                    currentPlayer.Index = 0;
                    currentPlayer.Clickable = true;
                }
            }
            return best;
        }
        else
        {
            int best = 1000;
            for (int i = 0; i < Board.Count; i++)
            {
                if (Board[i].GetComponent<TicTac>().Clickable)
                {
                    TicTac currentPlayer = Board[i].GetComponent<TicTac>();
                    currentPlayer.Index = 2;
                    currentPlayer.Clickable = false;
                    int tempbest = Mathf.Min(best, Minimax(Board, depth + 1, !minimax));
                    best = Mathf.Min(best, tempbest);
                    currentPlayer.Index = 0;
                    currentPlayer.Clickable = true;
                }
            }
            return best;
        }
    }
}
        
	

I wish upon A-Star:



  • Description: After using lots of pre-made pathfinding, I wanted to dig a bit deeper into how pathfinding works. I chose to try a pathfinding called A* (A star). The pathfinding can use an algorithm called "The Manhattan Distance" which is named after the island in Amerika due how every building is placed like in a grid. I had lots of fun making it and it works as intended (on a 2D plane). The red squares are the path, blue are "blockers", green is goal, big red is start.


  • Made in: Unity.


  • Time: 5 Days.



          		
  
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Node {

    public int GridX, GridY, MoveCost, ManhattanCost;
    public bool isWall;
    public Vector3 Position;
    public Node Parent;

    public int FCost
    {
        get
        {
            return MoveCost + ManhattanCost;
        }
    }

    public Node(bool p_isAWall, Vector3 p_position, int p_gridX, int p_gridY)
    {
        isWall = p_isAWall;
        Position = p_position;
        GridX = p_gridX;
        GridY = p_gridY;
    }
}
        
	
          		
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Pathfinding : MonoBehaviour
{
    ToGoScript Grid;
    private Node StartNode;
    private Node EndNode;
    private int MoveCost; 
    private List<Node> OpenList = new List<Node>();
    List<Node> Finalpath = new List<Node>();
    Node CurrentNode;
    private HashSet<Node> ClosedList = new HashSet<Node>();

    private void Awake()
    {
        Grid = GetComponent<ToGoScript>();    
    }
    private void Update()
    {
        OpenList = new List<Node>();
        ClosedList = new HashSet<Node>();
        AStarPath(Grid.StartObject.transform.position, Grid.EndObject.transform.position);
    }
    void AStarPath(Vector3 StartPos, Vector3 EndPos)
    {
        StartNode = Grid.NodeFromWorldPosition(StartPos);
        OpenList.Add(StartNode);      
        EndNode = Grid.NodeFromWorldPosition(EndPos);
        while (OpenList.Count > 0)
        {
            CurrentNode = OpenList[0];
            for (int i = 1; i < OpenList.Count; i++)
            {
                if (OpenList[i].FCost < CurrentNode.FCost || OpenList[i].FCost == CurrentNode.FCost && OpenList[i].ManhattanCost < CurrentNode.ManhattanCost)
                {
                    CurrentNode = OpenList[i];
                }
            }
            OpenList.Remove(CurrentNode);
            ClosedList.Add(CurrentNode);
            if (CurrentNode == EndNode)
            {
                GetFinalPath(StartNode, EndNode);
                break;
            }
            foreach (Node NeighbourNodes in Grid.GetNodes(CurrentNode))
            {
                if (NeighbourNodes.isWall || ClosedList.Contains(NeighbourNodes))
                {
                    continue;
                }
                MoveCost = CurrentNode.MoveCost + ManhattanDistance(CurrentNode, NeighbourNodes);
                if (MoveCost < NeighbourNodes.MoveCost || !OpenList.Contains(NeighbourNodes))
                {
                    NeighbourNodes.MoveCost = MoveCost;
                    NeighbourNodes.ManhattanCost = ManhattanDistance(NeighbourNodes, EndNode);
                    NeighbourNodes.Parent = CurrentNode;
                    if (!OpenList.Contains(NeighbourNodes))
                    {
                        OpenList.Add(NeighbourNodes);
                    }
                }
            }
        }
    }
    void GetFinalPath(Node StartNode, Node EndNode)
    {
        Finalpath.Clear();
        CurrentNode = EndNode;
        while (CurrentNode != StartNode)
        {
            Finalpath.Add(CurrentNode);
            CurrentNode = CurrentNode.Parent;
        }
        Finalpath.Reverse();
        Grid.FinalPath = Finalpath;
    }
    int ManhattanDistance(Node p_NodeA, Node p_NodeB)
    {
        return Mathf.Abs(p_NodeA.GridX - p_NodeB.GridX) + Mathf.Abs(p_NodeA.GridY - p_NodeB.GridY);
    }
}
        
	
          		
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ToGoScript : MonoBehaviour
{
    public LayerMask WallMask;
    private Vector2 gridWorldSize = new Vector2(30, 30);
    private float nodeRaidus = 0.5f;
    private float Distance = 0.0f;
    Node[,] grid;
    [HideInInspector]
    public List<Node> FinalPath = new List<Node>();
    private List<Vector3> nodePosition = new List<Vector3>();
    List<Node> NeighbouringNodes = new List<Node>();
    [HideInInspector]
    public GameObject StartObject;
    [HideInInspector]
    public GameObject EndObject;

    public float UpdatePosition = 50f;
    
    float nodeDiameter;
    int GridSizeX, GridSizeY;
    int xCheck, yCheck;
    int randPos = 0;

    private void Start()
    {
        StartObject = Resources.Load("StartPos") as GameObject ;
        EndObject = Resources.Load("EndPos") as GameObject;
        StartObject = Instantiate<GameObject>(StartObject, Vector3.zero, transform.rotation);
        EndObject = Instantiate<GameObject>(EndObject, Vector3.zero, transform.rotation);
        
        nodeDiameter = nodeRaidus + nodeRaidus;
        GridSizeY = Mathf.RoundToInt(gridWorldSize.y / nodeDiameter);
        GridSizeX = Mathf.RoundToInt(gridWorldSize.x / nodeDiameter);
        InvokeRepeating("UpdateLocation", UpdatePosition, UpdatePosition);

        CreateGrid();
        StartObject.transform.position = RandomPosition();
        EndObject.transform.position = RandomPosition();
    }
    void UpdateLocation()
    {
        StartObject.transform.position = RandomPosition();
        EndObject.transform.position = RandomPosition();
    }
    Vector3 RandomPosition()
    {
        if (nodePosition.Count < 1)
        {
            foreach (Node node in grid)
            {
                if (!node.isWall)
                    nodePosition.Add(node.Position);
            }
        }
        randPos = Random.Range(0, nodePosition.Count);

        return nodePosition[randPos];
    }
    void CreateGrid()
    {
        grid = new Node[Mathf.RoundToInt(GridSizeX), Mathf.RoundToInt(GridSizeY)];
        Vector3 bottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.forward * gridWorldSize.y / 2;

        for (int x = 0; x < GridSizeX; x++)
        {
            for (int y = 0; y < GridSizeY; y++)
            {
                Vector3 worldPoint = bottomLeft + Vector3.right * (x * nodeDiameter + nodeRaidus) + Vector3.forward * (y * nodeDiameter + nodeRaidus);
                bool Wall = false;

                if (Physics.CheckSphere(worldPoint, nodeRaidus, WallMask))
                {
                    Wall = true;
                }
                grid[x, y] = new Node(Wall, worldPoint, x, y);
            }
        }
    }
    bool CheckNextNode(int gridX, int gridY)
    {
        if (gridX >= 0 && gridX < GridSizeX)
        {
            if (gridY >= 0 && gridY < GridSizeY)
            {
                return true;
            }
        }
        return false;
    }
    public List<Node> GetNodes(Node p_Node)
    {
        NeighbouringNodes.Clear();
        xCheck = p_Node.GridX + 1;
        yCheck = p_Node.GridY;
        if(CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX - 1;
        yCheck = p_Node.GridY;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX + 1;
        yCheck = p_Node.GridY + 1;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX + 1;
        yCheck = p_Node.GridY - 1;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX - 1;
        yCheck = p_Node.GridY + 1;

        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX - 1;
        yCheck = p_Node.GridY - 1;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        xCheck = p_Node.GridX;
        yCheck = p_Node.GridY + 1;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }

        xCheck = p_Node.GridX;
        yCheck = p_Node.GridY - 1;
        if (CheckNextNode(xCheck, yCheck))
        {
            NeighbouringNodes.Add(grid[xCheck, yCheck]);
        }
        return NeighbouringNodes;
    }
    public Node NodeFromWorldPosition(Vector3 p_worldPos)
    {
        return grid[Mathf.RoundToInt((GridSizeX - 1) * ((p_worldPos.x + gridWorldSize.x / 2) / gridWorldSize.x)),
            Mathf.RoundToInt((GridSizeY - 1) * ((p_worldPos.z + gridWorldSize.y / 2) / gridWorldSize.y))];
    }
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireCube(transform.position, new Vector3(gridWorldSize.x, 1, gridWorldSize.y));
        if (grid != null)
        {
            foreach (Node node in grid)
            {
                if (node.isWall)
                {
                    Gizmos.color = Color.blue;
                }
                else
                {
                    Gizmos.color = Color.yellow;
                }
                if (FinalPath != null)
                {
                    if (FinalPath.Contains(node))
                    {
                        Gizmos.color = Color.red;
                    }
                }
                Gizmos.DrawCube(node.Position, Vector3.one * (nodeDiameter - Distance));
            }
        }
    }
}
        
	

Local ecosystem:



  • Description: I wanted to create an ecosystem that can live on its own. The grass can grow and decay. The higher the grass is, the more food the sheep will get from eating it. The wolf hunts the sheep and always choose the sheep that is the most fed. If the sheep or wolf doesn't eat, they will die. They cannot eat from the same patch of grass. If they eat too much, they will give birth to a new animal. The objects are using state machine.


(WARNING: It is an older code that is not pretty. Lots of hardcoding and ugly variables.. It was made a long time ago, at the beginning of my code career. The idea was fun, so I thought it belongs here!)


  • Made in: Unity.


  • Time: 2 Weeks.


Behaviour Tree:



  • Description: I have always been fascinated about behaviour trees. Usually I only uses state machine because I find them very easy to use and to understand. But behaviour tree is something I wanted to learn to use and to create so why not create my own? First, I did lots of research about how a behaviour tree works. So, I start with creating the logic behind the tree. I create a Selector, a Sequence, Repeator and a Routine. Second, I create the tree so that it fits the gif to the right. The cube cooks pancakes but need egg and milk. The blue gets the milk when the milk is empty. The green gets the egg when red is out of eggs. The behaviour between blue and green are the same but could easily be changed into doing something more exciting.


  • Made in: Unity.


  • Time: 2 Weeks.


FRAUS:



  • Description: My first AI. The player controls the mainframe of the game so every character was being controlled by an AI. I made a state machine for the various characters in the game. There are 3 different types of characters: Guards, Scientist and Prisoners. The objective is to help the prisoners escape. The guards are patrolling and if they see a prisoner withing their vision field, they make the prisoner freeze, run up to them and escort them to a prison pod, safely transfer them back into their cell. The scientist cannot fight, but if they see a prisoner on the loose, they will run to nearest guard and make them run to the position where the spotted the prisoner


  • Made in: Unity.


  • Time: 8 Weeks.


  • The game won an award on Gotland Game Conference (GGC). The award is called "Almedalspriset" which is a reward for creating a fun and repetative game and was displayed in Gotlands biggest library in 2 years.


(The code is not for display unfortunally)

(more coming soon)

FolkeLogo