Thanks Our coding styles are, essentially, very similar. Just don't use global variables anymore (as you have a video about it, you already know how to do this in C; at least one possible way), and keep some better habits and discipline with regard to variable and constant names. You can also switch to Rust. It has a default coding style (rustfmt) built in, and if you don't keep to it, it will nag you until you domaksimKorzh wrote: ↑Tue Dec 08, 2020 8:36 pm Marcel, I always enjoy reading your code snippets))) It's like if I was reading my own code but if I had a better habits)
Complicating code in C#
Moderators: hgm, Rebel, chrisw
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Complicating code in C#
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Complicating code in C#
My experience is that I always regret it when I did not use an interface.
Use interface for hiding representation and polymorphism.
Now I have this:
Use interface for hiding representation and polymorphism.
Now I have this:
Code: Select all
public struct BitBoardCoord : ICoord
{
public int Value
{
get;
private set;
}
public BitBoardCoord(ulong bit)
{
if (bit == 0) Value = -1;
Value = (int)Math.Log(bit, 2);
}
public BitBoardCoord(coord coord)
{
Value = (int)coord;
}
public override string ToString()
{
return ((coord)Value).ToString();
}
}
public interface ICoord
{
int Value
{
get;
}
}
Last edited by Henk on Thu Dec 10, 2020 6:01 pm, edited 2 times in total.
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Complicating code in C#
Why do you need coordinates anyway? Chess engines don't use coordinates internally, most of the time. Square numbers from 0 to and including 63 is more efficient. If you need coordinates as a reference (for example E1 and G1 for castling, that sort of stuff) just define a few constants with the correct square number.
I have this struct, which just collects a bunch of constants under one name:
Code: Select all
pub struct Squares;
impl Squares {
// White side squares that are important for castling
pub const A1: Square = 0;
pub const B1: Square = 1;
pub const C1: Square = 2;
pub const D1: Square = 3;
pub const E1: Square = 4;
pub const F1: Square = 5;
pub const G1: Square = 6;
pub const H1: Square = 7;
// Black side squares that are important for castling
pub const A8: Square = 56;
pub const B8: Square = 57;
pub const C8: Square = 58;
pub const D8: Square = 59;
pub const E8: Square = 60;
pub const F8: Square = 61;
pub const G8: Square = 62;
pub const H8: Square = 63;
// White EP-squares start/end
pub const A3: Square = 16;
pub const H3: Square = 23;
// Black EP-squares start/end
pub const A6: Square = 40;
pub const H6: Square = 47;
}
Code: Select all
const EP_SQUARES_WHITE: RangeInclusive<Square> = Squares::A3..=Squares::H3;
const EP_SQUARES_BLACK: RangeInclusive<Square> = Squares::A6..=Squares::H6;
You are making things much more difficult for yourself than you need to. If you'd want to, the only things you need in a chess engine are strings, characters, and some ints. (And maybe some derivative types such as arrays of ints, and the like.)
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Complicating code in C#
When someone says I want to play capablanca chess or three dimensional chess or whatever idiotic chess variation then you want to keep thinks similar to reduce complexity.mvanthoor wrote: ↑Thu Dec 10, 2020 5:54 pmWhy do you need coordinates anyway? Chess engines don't use coordinates internally, most of the time. Square numbers from 0 to and including 63 is more efficient. If you need coordinates as a reference (for example E1 and G1 for castling, that sort of stuff) just define a few constants with the correct square number.
I have this struct, which just collects a bunch of constants under one name:
And then, in my FEN-reader, for example:Code: Select all
pub struct Squares; impl Squares { // White side squares that are important for castling pub const A1: Square = 0; pub const B1: Square = 1; pub const C1: Square = 2; pub const D1: Square = 3; pub const E1: Square = 4; pub const F1: Square = 5; pub const G1: Square = 6; pub const H1: Square = 7; // Black side squares that are important for castling pub const A8: Square = 56; pub const B8: Square = 57; pub const C8: Square = 58; pub const D8: Square = 59; pub const E8: Square = 60; pub const F8: Square = 61; pub const G8: Square = 62; pub const H8: Square = 63; // White EP-squares start/end pub const A3: Square = 16; pub const H3: Square = 23; // Black EP-squares start/end pub const A6: Square = 40; pub const H6: Square = 47; }
So I can say things like (pseudocode): "if square in EP_SQUARES_WHITE { .... }"Code: Select all
const EP_SQUARES_WHITE: RangeInclusive<Square> = Squares::A3..=Squares::H3; const EP_SQUARES_BLACK: RangeInclusive<Square> = Squares::A6..=Squares::H6;
You are making things much more difficult for yourself than you need to. If you'd want to, the only things you need in a chess engine are strings, characters, and some ints. (And maybe some derivative types such as arrays of ints, and the like.)
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Complicating code in C#
I think enum below is terrible code as well. Mixing pieceColor with pieceKind.
Maybe better use a struct
Piece {
int Color;
int Kind;
}
or something.
Code: Select all
public enum PieceType2 { whitePawn, whiteKnight, whiteBishop, whiteRook, whiteQueen, whiteKing, blackPawn, blackKnight, blackBishop, blackRook, blackQueen, blackKing, none }
Piece {
int Color;
int Kind;
}
or something.
-
- Posts: 1563
- Joined: Thu Jul 16, 2009 10:47 am
- Location: Almere, The Netherlands
Re: Complicating code in C#
There is noting wrong with using an enum, if you insist on writing inefficient code use the struct.Henk wrote: ↑Fri Dec 11, 2020 11:16 am I think enum below is terrible code as well. Mixing pieceColor with pieceKind.
Maybe better use a structCode: Select all
public enum PieceType2 { whitePawn, whiteKnight, whiteBishop, whiteRook, whiteQueen, whiteKing, blackPawn, blackKnight, blackBishop, blackRook, blackQueen, blackKing, none }
Piece {
int Color;
int Kind;
}
or something.
I use the lowest bit for color information, like:
Code: Select all
enum PieceType { White, Black, whitePawn, blackPawn, whiteKnight, blackKnight, etc. };
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Complicating code in C#
Yes its efficiency. Or maybe use a number and do an extra lookup to get the properties of the piece.
enum won't work when N players play a game with M different pieces where N, M large.
enum won't work when N players play a game with M different pieces where N, M large.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
-
- Posts: 1563
- Joined: Thu Jul 16, 2009 10:47 am
- Location: Almere, The Netherlands
Re: Complicating code in C#
I really don't understand what you are trying to achieve, why complicate matters so much, it is all a piece of cake.
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Complicating code in C#
One way to control code cmplexity is having a certain mindset. In my case...
"Do I really NEED to use this feture in the programming language to write this piece of code?"
That means, for exmple: I *could* build an interface for my board implementation, but as I don't intend to provide multiple board representations, I don't write one. I DO provide multiple communication protocols (uci and xboard), so I DO write an interface for my Comm module. The engine can either attach the uci or xboard module when it starts. I have no interface for Search (alpha/beta only), but if I decide to add MCTS, I will add the interface as well and make bot the A/B-search and MCTS-search conform to it, so my engine can load either at startup.
"Do I really NEED to use a library for this?"
Yes, I could use a library for some things in my engine, but if I don't have to, I don't. When do I use a library?
- If the library provides better/faster functionality than the standard library (crossbeam-channel vs. STD channels in Rust)
- If I am not confident that I can write my own code better or more robust (such as a good random number generator)
- If the library provides functionality that makes my code shorter / cleaner / easier to read (if-chain, in Rust)
- If writing the code myself is a lot of work, but not interesting for me (probing end game table bases)
- If the library makes it easy to do something that would be a lot of work to do well (the command line parser CLAP, for example)
One other criterion for me to include a library into my engine is that it either:
- Is very well known and very well supported (such as CLAP, rand, crossbeam-channel)
OR
- Is very easy to get rid of and/or replace should I want to (if-chain; even though I don't see that changing anytime soon, as long as Rust's macro parser doesn't change)
This keeps the used programming language features and third-party code in your engine to a bare minimum. That means you have more control over the code that ends up in your engine, and you won't get into the 'crap this library is outdated and not updated anymore' scenario quite as often (if ever). All of this makes your code less complex, and more self-sufficient.
Everything else with regard to complexity is something you either avoid, or design into your engine yourself.
"Do I really NEED to use this feture in the programming language to write this piece of code?"
That means, for exmple: I *could* build an interface for my board implementation, but as I don't intend to provide multiple board representations, I don't write one. I DO provide multiple communication protocols (uci and xboard), so I DO write an interface for my Comm module. The engine can either attach the uci or xboard module when it starts. I have no interface for Search (alpha/beta only), but if I decide to add MCTS, I will add the interface as well and make bot the A/B-search and MCTS-search conform to it, so my engine can load either at startup.
"Do I really NEED to use a library for this?"
Yes, I could use a library for some things in my engine, but if I don't have to, I don't. When do I use a library?
- If the library provides better/faster functionality than the standard library (crossbeam-channel vs. STD channels in Rust)
- If I am not confident that I can write my own code better or more robust (such as a good random number generator)
- If the library provides functionality that makes my code shorter / cleaner / easier to read (if-chain, in Rust)
- If writing the code myself is a lot of work, but not interesting for me (probing end game table bases)
- If the library makes it easy to do something that would be a lot of work to do well (the command line parser CLAP, for example)
One other criterion for me to include a library into my engine is that it either:
- Is very well known and very well supported (such as CLAP, rand, crossbeam-channel)
OR
- Is very easy to get rid of and/or replace should I want to (if-chain; even though I don't see that changing anytime soon, as long as Rust's macro parser doesn't change)
This keeps the used programming language features and third-party code in your engine to a bare minimum. That means you have more control over the code that ends up in your engine, and you won't get into the 'crap this library is outdated and not updated anymore' scenario quite as often (if ever). All of this makes your code less complex, and more self-sufficient.
Everything else with regard to complexity is something you either avoid, or design into your engine yourself.