Nonexistent function in C-Sharp script

Godot Version

4.2.1

Question

I’m trying to run a method in a C-Sharp script and Godot is not able to find it. I’m passing the class a parameter to a method, and in this method, I’m trying to access GetPiecesIndicesByColor() function. The response is that the function does not exist, but the method is there.

public List<int> GetPiecesIndicesByColor(Piece.Color color)
{
    switch (color)
    {
        case Piece.Color.White:
            return whitePiecesIndices;
        case Piece.Color.Black:
            return blackPiecesIndices;
    }
    return null;
}

At the same time, Godot can access a different method in the same class (GetTurnColor()). Why is this happening?

The place in gdscript I’m trying to access the methods. The first one return a value, the second one fails.

print_debug(board.GetTurnColor())
print_debug(board.GetPiecesIndicesByColor(board.GetTurnColor()))

The error message is like this:

image

GetTurnColor()

don’t exist or you don’t show in code to us

This method works, it’s GetPiecesIndicesByColor() that is failing.

You need to cast it to your type, you can’t do method calling like in GDScript, see here

GetTurnColor()

ok but what returns?

It returns an int:

public Piece.Color GetTurnColor()
    {
        return currentState.turnColor;
    }

hmm can paste whole Board.cs ?

using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public partial class Board : Node
{
    // Piece struct
    public struct Piece
    {
        public enum Type
        {
            None,
            King,
            Queen,
            Bishop,
            Knight,
            Rook,
            Pawn
        }
        public enum Color
        {
            None,
            White,
            Black
        }
        
        public Type type;
        public Color color;
    }
    // Move struct
    public struct Move
    {
        // Move flags
        public enum Flags
        {
            None,
            DoublePush,
            Promotion,
            EnPassant,
            CastleShort,
            CastleLong
        }
        // For every move
        public int squareSourceIndex, squareTargetIndex;
        public Piece pieceSource, pieceTarget;
        public Flags flags;

        // Promotion
        public Piece.Type promotionPieceType;
    }
    // State struct
    public struct State
    {
        public Piece.Color turnColor;
        public Piece.Color doublePushedPawnColor; // Color of pawn that doublepushed for check if enPassant is available
        public int enPassantSquareIndex; // Pawn tile that can be captured by en passant
        public bool canCastleWhite, canCastleShortWhite, canCastleLongWhite;
        public bool canCastleBlack, canCastleShortBlack, canCastleLongBlack;
    }

    // Start fen string
    public static readonly string StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -";

    // Towers squares & king squares
    public static readonly int A1 = 56, H1 = 63, A8 = 0, H8 = 7; // Tower squares
    public static readonly int E1 = 60, E8 = 4; // King squares
    public static readonly int C1 = 58, C8 = 2, G1 = 62, G8 = 6;
    public static readonly int F1 = 61, D1 = 59, F8 = 5, D8 = 3; // Tower castling target squares
    public static readonly int B1 = 57, B8 = 1;

    // Promotion piece
    public Piece.Type PromotionPieceType = Piece.Type.Queen;

    // Pieces array
    private Piece[] pieces = new Piece[64];
    private List<int> whitePiecesIndices = new List<int>();
    private List<int> blackPiecesIndices = new List<int>();

    // Stack holding the moves and the states of the game as moves are been played
    private Stack<Move> moves = new Stack<Move>();
    private Stack<State> states = new Stack<State>();

    private State currentState = new State();

    // Get piece at index
    public Piece GetPiece(int index)
    {
        return pieces[index];
    }
    
    // Get pieces indices from color
    public List<int> GetPiecesIndicesByColor(Piece.Color color)
    {
        switch (color)
        {
            case Piece.Color.White:
                return whitePiecesIndices;
            case Piece.Color.Black:
                return blackPiecesIndices;
        }
        return null;
    }
    
    // Get last move
    public bool TryGetLastMove(out Move move)
    {
        return moves.TryPeek(out move);
    }
    // Get state
    public ref readonly State GetState()
    {
        return ref currentState;
    }
    // Get turn color
    public Piece.Color GetTurnColor()
    {
        return currentState.turnColor;
    }
    
    // Find king
    public int FindKingOfColor(Piece.Color color)
    {
        List<int> indices = GetPiecesIndicesByColor(color);
        foreach (int index in indices)
        {
            if (pieces[index].type == Piece.Type.King)
            {
                return index;
            }
        }
        GD.PrintErr("there is no king?");
        return 0;
    }

    // Copy board pieces and current state to other board
    public void CopyBoardState(Board board)
    {
        pieces.CopyTo(board.pieces, 0);
        board.currentState = currentState;
        board.whitePiecesIndices = whitePiecesIndices.ToList();
        board.blackPiecesIndices = blackPiecesIndices.ToList();
    }

    // Set FEN string
    public void LoadFEN(string fen)
    {
        // Clear
        states.Clear();
        moves.Clear();
        whitePiecesIndices.Clear();
        blackPiecesIndices.Clear();

        // Split fen string
        string[] subFEN = fen.Split(' ');

        // Pieces placement (subFEN[0])
        Dictionary<char, Piece.Type> pieceTypeFromSymbol = new Dictionary<char, Piece.Type>()
        {
            { 'p', Piece.Type.Pawn }, { 'n', Piece.Type.Knight }, { 'b', Piece.Type.Bishop },
            { 'r', Piece.Type.Rook }, { 'q', Piece.Type.Queen  }, { 'k', Piece.Type.King   }
        };
        
        int i = 0, j = 0;
        foreach (char symbol in subFEN[0])
        {
            if (symbol == '/')
            {
                i = 0;
                j++;
            }
            else if (char.IsDigit(symbol))
            {
                int n = symbol - '0'; // Number of empy squares
                for (int ii = 0; ii < n; ii++)
                {
                    pieces[(i + ii) + j * 8] = new Piece(); // "none" piece
                }
                i += n;
            }
            else
            {
                int index = i + j * 8;
                Piece.Type pieceType = pieceTypeFromSymbol[char.ToLower(symbol)]; // must be a piece symbol then
                Piece.Color pieceColor = char.IsUpper(symbol) ? Piece.Color.White : Piece.Color.Black;
                pieces[index] = new Piece { type = pieceType, color = pieceColor };
                i++;

                // Pieces list
                switch (pieceColor)
                {
                    case Piece.Color.White:
                        whitePiecesIndices.Add(index);
                        break;
                    case Piece.Color.Black:
                        blackPiecesIndices.Add(index);
                        break;
                }
            }

        }
        // Turn color (subFEN[1])
        currentState.turnColor = subFEN[1].Equals("w") ? Piece.Color.White : Piece.Color.Black;

        // Castling rights (subFEN[2])
        currentState.canCastleShortWhite = false;
        currentState.canCastleLongWhite = false;
        currentState.canCastleShortBlack = false;
        currentState.canCastleLongBlack = false;

        if (subFEN[2].Equals("-"))
        {
            // Neither side can castle
            currentState.canCastleWhite = false;
            currentState.canCastleBlack = false;
        }
        else
        {
            foreach (char symbol in subFEN[2])
            {
                switch (symbol)
                {
                    case 'K':
                        currentState.canCastleShortWhite = true;
                        break;
                    case 'Q':
                        currentState.canCastleLongWhite = true;
                        break;
                    case 'k':
                        currentState.canCastleShortBlack = true;
                        break;
                    case 'q':
                        currentState.canCastleLongBlack = true;
                        break;
                }
            }

            currentState.canCastleWhite = currentState.canCastleShortWhite || currentState.canCastleLongWhite;
            currentState.canCastleBlack = currentState.canCastleShortBlack || currentState.canCastleLongBlack;
        }
        // En passant (subFEN[3])
        if (subFEN[3].Equals("-"))
        {
            currentState.doublePushedPawnColor = Piece.Color.None;
            currentState.enPassantSquareIndex = 0;
        }
        else
        {
            // The format is then the char of the column & the number of the row
            int column = subFEN[3][0] - 'a';
            int row = 0;

            switch (subFEN[3][1])
            {
                case '3':
                    row = 4;
                    currentState.doublePushedPawnColor = Piece.Color.White;
                    break;
                case '6':
                    row = 3;
                    currentState.doublePushedPawnColor = Piece.Color.Black;
                    break;
            }
            currentState.enPassantSquareIndex = column + row * 8;
        }
    }

    // Get fen
    public string GetFEN()
    {
        Dictionary<Piece.Type, char> symbolFromPieceType = new Dictionary<Piece.Type, char>()
        {
            { Piece.Type.Pawn, 'p' }, { Piece.Type.Knight, 'n' }, { Piece.Type.Bishop, 'b' },
            { Piece.Type.Rook, 'r' }, { Piece.Type.Queen , 'q' }, { Piece.Type.King  , 'k' }
        };

        StringBuilder fenString = new StringBuilder();

        // Pieces
        for (int j = 0; j < 8; j++)
        {
            int emptyCounter = 0;
            for (int i = 0; i < 8; i++)
            {
                int index = i + j * 8;
                Piece piece = pieces[index];
                
                if (piece.type != Piece.Type.None)
                {
                    if (emptyCounter != 0)
                    {
                        fenString.Append(emptyCounter);
                    }
                    
                    char pieceSymbol = piece.color == Piece.Color.White ? char.ToUpper(symbolFromPieceType[piece.type]) : symbolFromPieceType[piece.type];
                    fenString.Append(pieceSymbol);

                    emptyCounter = 0;
                }
                else
                {
                    emptyCounter++;
                }
            }
            
            if (emptyCounter != 0)
            {
                fenString.Append(emptyCounter);
            }

            if (j < 7)
            {
                fenString.Append('/');
            }
        }
        
        // Turn color
        char turnColor = currentState.turnColor == Piece.Color.White ? 'w' : 'b';
        fenString.AppendFormat(" {0} ", turnColor);

        // Castling rights
        if (currentState.canCastleWhite || currentState.canCastleBlack)
        {
            if (currentState.canCastleWhite)
            {
                if (currentState.canCastleShortWhite)
                {
                    fenString.Append('K');
                }
                if (currentState.canCastleLongWhite)
                {
                    fenString.Append('Q');
                }

            }
            if (currentState.canCastleBlack)
            {
                if (currentState.canCastleShortBlack)
                {
                    fenString.Append('k');
                }
                if (currentState.canCastleLongBlack)
                {
                    fenString.Append('q');
                }
            }
        }
        else
        {
            fenString.Append("-");
        }

        // En passant
        if (currentState.doublePushedPawnColor != Piece.Color.None)
        {
            char[] letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
            int column = currentState.enPassantSquareIndex % 8;
            char columnLetter = letters[column];

            switch (currentState.doublePushedPawnColor)
            {
                case Piece.Color.White:
                    fenString.AppendFormat(" {0}{1}", columnLetter, 4);
                    break;
                case Piece.Color.Black:
                    fenString.AppendFormat(" {0}{1}", columnLetter, 6);
                    break;
            }
        }
        else
        {
            fenString.Append(" -");
        }
        return fenString.ToString();
    }

    // Make a move on the board
    public void MakeMove(Move move)
    {
        // Push the current state into the stack
        states.Push(currentState);

        // Modify pieces indices list
        switch (move.pieceSource.color)
        {
            case Piece.Color.White:
                whitePiecesIndices.Remove(move.squareSourceIndex);
                whitePiecesIndices.Add(move.squareTargetIndex);
                break;
            case Piece.Color.Black:
                blackPiecesIndices.Remove(move.squareSourceIndex);
                blackPiecesIndices.Add(move.squareTargetIndex);
                break;
        }

        switch (move.pieceTarget.color)
        {
            case Piece.Color.White:
                whitePiecesIndices.Remove(move.squareTargetIndex);
                break;
            case Piece.Color.Black:
                blackPiecesIndices.Remove(move.squareTargetIndex);
                break;
        }

        // Make the move & change state
        pieces[move.squareSourceIndex] = new Piece(); // Remove piece at the "Source" square

        // Move flags

        switch (move.flags)
        {
            case Move.Flags.DoublePush:
                currentState.doublePushedPawnColor = move.pieceSource.color;
                currentState.enPassantSquareIndex = move.squareTargetIndex;
                pieces[move.squareTargetIndex] = move.pieceSource;
                break;
            case Move.Flags.Promotion:
                currentState.doublePushedPawnColor = Piece.Color.None;
                pieces[move.squareTargetIndex] = new Piece { type = move.promotionPieceType, color = move.pieceSource.color };
                break;
            case Move.Flags.EnPassant:
                switch (currentState.doublePushedPawnColor)
                {
                    case Piece.Color.White:
                        whitePiecesIndices.Remove(currentState.enPassantSquareIndex);
                        break;
                    case Piece.Color.Black:
                        blackPiecesIndices.Remove(currentState.enPassantSquareIndex);
                        break;
                }

                pieces[currentState.enPassantSquareIndex] = new Piece();
                currentState.doublePushedPawnColor = Piece.Color.None;
                pieces[move.squareTargetIndex] = move.pieceSource;

                break;
            case Move.Flags.CastleShort:
                currentState.doublePushedPawnColor = Piece.Color.None;
                pieces[move.squareSourceIndex] = new Piece();
                pieces[move.squareTargetIndex] = move.pieceSource;

                switch (move.pieceSource.color)
                {
                    case Piece.Color.White:
                        pieces[F1] = pieces[H1];
                        pieces[H1] = new Piece();

                        whitePiecesIndices.Remove(H1);
                        whitePiecesIndices.Add(F1);
                        break;
                    case Piece.Color.Black:
                        pieces[F8] = pieces[H8];
                        pieces[H8] = new Piece();

                        blackPiecesIndices.Remove(H8);
                        blackPiecesIndices.Add(F8);
                        break;
                }
                break;
            case Move.Flags.CastleLong:
                currentState.doublePushedPawnColor = Piece.Color.None;
                pieces[move.squareSourceIndex] = new Piece();
                pieces[move.squareTargetIndex] = move.pieceSource;

                switch (move.pieceSource.color)
                {
                    case Piece.Color.White:
                        pieces[D1] = pieces[A1];
                        pieces[A1] = new Piece();

                        whitePiecesIndices.Remove(A1);
                        whitePiecesIndices.Add(D1);
                        break;
                    case Piece.Color.Black:
                        pieces[D8] = pieces[A8];
                        pieces[A8] = new Piece();

                        blackPiecesIndices.Remove(A8);
                        blackPiecesIndices.Add(D8);
                        break;
                }
                break;
            default:
                currentState.doublePushedPawnColor = Piece.Color.None;
                pieces[move.squareTargetIndex] = move.pieceSource;
                break;
        }

        // Check if the king or the towers moved
        if (move.squareSourceIndex == E1 || move.squareTargetIndex == E1) // White king
        {
            currentState.canCastleWhite = false;
        }
        else if (move.squareSourceIndex == E8 || move.squareTargetIndex == E8) // Black king
        {
            currentState.canCastleBlack = false;
        }
        if (move.squareSourceIndex == A1 || move.squareTargetIndex == A1) // White queen side tower
        {
            currentState.canCastleLongWhite = false;
        }
        else if (move.squareSourceIndex == H1 || move.squareTargetIndex == H1) // White king side tower
        {
            currentState.canCastleShortWhite = false;
        }
        if (move.squareSourceIndex == A8 || move.squareTargetIndex == A8) // Black queen side tower
        {
            currentState.canCastleLongBlack = false;
        }
        else if (move.squareSourceIndex == H8 || move.squareTargetIndex == H8) // Black king side tower
        {
            currentState.canCastleShortBlack = false;
        }

        // Push the move into the stack
        moves.Push(move);

        // Change turn color
        currentState.turnColor = currentState.turnColor == Piece.Color.White ? Piece.Color.Black : Piece.Color.White;
    }

    public void UndoMove()
    {
        if (states.Count > 0)
        {
            // Get back to the last state
            currentState = states.Pop();

            // Get the last move and undo it
            Move move = moves.Pop();

            // Modify pieces indices list
            switch (move.pieceSource.color)
            {
                case Piece.Color.White:
                    whitePiecesIndices.Remove(move.squareTargetIndex);
                    whitePiecesIndices.Add(move.squareSourceIndex);
                    break;
                case Piece.Color.Black:
                    blackPiecesIndices.Remove(move.squareTargetIndex);
                    blackPiecesIndices.Add(move.squareSourceIndex);
                    break;
            }

            switch (move.pieceTarget.color)
            {
                case Piece.Color.White:
                    whitePiecesIndices.Add(move.squareTargetIndex);
                    break;
                case Piece.Color.Black:
                    blackPiecesIndices.Add(move.squareTargetIndex);
                    break;
            }

            // Undo move
            pieces[move.squareSourceIndex] = move.pieceSource;
            pieces[move.squareTargetIndex] = move.pieceTarget;

            switch (move.flags)
            {
                case Move.Flags.CastleShort:
                    switch (move.pieceSource.color)
                    {
                        case Piece.Color.White:
                            pieces[F1] = new Piece();
                            pieces[H1] = new Piece { type = Piece.Type.Rook, color = Piece.Color.White };

                            whitePiecesIndices.Remove(F1);
                            whitePiecesIndices.Add(H1);
                            break;
                        case Piece.Color.Black:
                            pieces[F8] = new Piece();
                            pieces[H8] = new Piece { type = Piece.Type.Rook, color = Piece.Color.Black };

                            blackPiecesIndices.Remove(F8);
                            blackPiecesIndices.Add(H8);
                            break;
                    }
                    break;
                case Move.Flags.CastleLong:
                    switch (move.pieceSource.color)
                    {
                        case Piece.Color.White:
                            pieces[D1] = new Piece();
                            pieces[A1] = new Piece { type = Piece.Type.Rook, color = Piece.Color.White };

                            whitePiecesIndices.Remove(D1);
                            whitePiecesIndices.Add(A1);
                            break;
                        case Piece.Color.Black:
                            pieces[D8] = new Piece();
                            pieces[A8] = new Piece { type = Piece.Type.Rook, color = Piece.Color.Black };

                            blackPiecesIndices.Remove(D8);
                            blackPiecesIndices.Add(A8);
                            break;
                    }
                    break;
                case Move.Flags.EnPassant:
                    pieces[currentState.enPassantSquareIndex] = new Piece { type = Piece.Type.Pawn, color = currentState.doublePushedPawnColor };

                    switch (currentState.doublePushedPawnColor)
                    {
                        case Piece.Color.White:
                            whitePiecesIndices.Add(currentState.enPassantSquareIndex);
                            break;
                        case Piece.Color.Black:
                            blackPiecesIndices.Add(currentState.enPassantSquareIndex);
                            break;
                    }
                    break;
            }
        }
    }
}

Where are you calling it from? Is it stored as a Board or Node or var?

If you’re calling from GDScript it’s possibly because you’re using an unsupported type for your return argument

See here and here

I’m calling like this:

var board_script:CSharpScript
var board:Node

func _ready():
	board_script = load("res://Board.cs")
	board = board_script.new() 
        print_debug(board.GetTurnColor())
        print_debug(board.GetPiecesIndicesByColor(board.GetTurnColor()))

(next time do remember to clarify you’re cross language scripting and that some of the code is GDScript)

2 Likes

In this case, since the method returns a list, should I use a Godot.Collections.Array instead in the C# script?

Or int[] I think, I’m not experienced with C# in Godot