Page 7 of 14

Re: Simplifying code

Posted: Fri Jul 31, 2020 6:34 pm
by odomobo
Henk wrote: Fri Jul 31, 2020 2:25 pm - Tried to write it functional. So recursion instead of loops.
Recursion instead of iteration is pretty bad in languages without tail-call recursion. And, honestly, tail-call recursion is a hack to allow functional languages to have better performance than they should.
Henk wrote: Fri Jul 31, 2020 2:25 pm - yes for debugging nested expressions is terrible. Although there will be more temporary variables with silly names.
Not just for debugging, but also for reasoning about the work that's being done. For example, in the return statement, if the conditionals were separated into their own variables, I might be able to understand was it was doing.

I know that some people advocate for functions with a single return statement, but I think multiple returns can greatly simplify certain functions. I think the better way to think about it is: try to structure your code such that functions will only require a single return statement.
Henk wrote: Fri Jul 31, 2020 2:25 pm Looks like Initial stupid code was best. Rewrite only makes it less natural or divergent from initial idea.
Its like gumming in a drawing. I remember teacher said use gum sparingly.
In my experience, sometimes you need to make a change in order to see that it's bad, and then revert all of your work. Even though it feels like a waste, it's necessary for forward progress, because it taught you something about the code base or about the problem you're solving.

There are a lot of different opinions on functional, but from my perspective, functional-style code can be good only for bits of your code where performance isn't the primary concern. This is because the declarative style can limit the control you have over implementation. However, I think you can benefit from the functional concepts even when you aren't writing true functional code.

For example, simply making most of your functions have no or very limited side effects will make it much easier to reason about your code.

Re: Simplifying code

Posted: Fri Jul 31, 2020 6:36 pm
by Pio
odomobo wrote: Thu Jul 30, 2020 9:07 pm Instead of calling .Aggregate(), you can call .Sum() to simplify. Even though the performance for perft isn't the biggest concern, there are 2 issues with using LinQ IMO:
  1. It creates additional objects, which need to be GCd.
  2. You lose some control over the sequence in which the code is executed.
It can be really convenient for things that aren't in a tight loop, though.
I agree, linq can be very slow, but when used correctly in non performance critical parts it can be very beautiful. I use the yield return in C# for my iterative search function so I could separate the XBoard/winboard printing from the chess/search logic. My search function returns a lazy evaluated array that is automatically printed for each new depth so the search can be completely decoupled from the GUI-interface 😁

Re: Simplifying code

Posted: Sun Aug 02, 2020 2:06 am
by OliverBr
Henk wrote: Fri Apr 24, 2020 9:11 pm
Henk wrote: Fri Apr 24, 2020 2:54 pm Can't copy position efficiently. Too many bytes.
Maybe only 32 bytes needed. So maybe not that inefficient.
But too late I already implemented my snapshot solution.
Undoing a move takes typically less data than 32bytes. I really would like to see your solution.

Re: Simplifying code

Posted: Sun Aug 02, 2020 3:00 am
by smcracraft
Exactly why I converted my program to 64-bit bitmaps for both move generation and position evaluation years ago.

It has made the entirety simple.

I love engineering simplicity!!!

Re: Simplifying code

Posted: Sun Aug 02, 2020 9:44 am
by Henk
OliverBr wrote: Sun Aug 02, 2020 2:06 am
Henk wrote: Fri Apr 24, 2020 9:11 pm
Henk wrote: Fri Apr 24, 2020 2:54 pm Can't copy position efficiently. Too many bytes.
Maybe only 32 bytes needed. So maybe not that inefficient.
But too late I already implemented my snapshot solution.
Undoing a move takes typically less data than 32bytes. I really would like to see your solution.

Code: Select all

public enum Sort { whitePawn, whiteKnight, whiteBishop, whiteRook,  whiteQueen, whiteKing, blackPawn, blackKnight, blackBishop, blackRook, blackQueen, blackKing, none }

UInt64[] pieces;
public UInt64 Other { get; set; }

 public ChessBoardBits()
        {
            pieces = new UInt64[(int)none];
            Init();
        }

        public void Init()
        {
            for(int i = 0; i < (int)none; i++)
            {
                pieces[i] = 0;
            }
            Other = 0;
        }
        
        private ChessBoardBits Copy()
        { 

            var piecesCopy = new UInt64[(int)none];
            for (int i = 0; i < (int)none; i++)
            {
                piecesCopy[i] = pieces[i];
            }
            return new ChessBoardBits(piecesCopy, Other);
        }

So copy-ing these bits would mean (12 + 1) x 8 = 13 * 8 = 104 bytes.
Can reduce this to (6 + 1 + 1) x 8 = 64 bytes by storing bitboard of white or black pieces.

Making a move including copy-ing a position is expensive operation in my program.

Also getting white/blackpieces very slow:

Code: Select all

  public UInt64 WhitePieces
        {
            get
            {
                return pieces[0] | pieces[1] | pieces[2] | pieces[3] | pieces[4] | pieces[5];
                
            }        
        }
        public UInt64 BlackPieces
        {
            get
            {
                return pieces[6] | pieces[7] | pieces[8] | pieces[9] | pieces[10] | pieces[11];
               
            }
        }
  

Re: Simplifying code

Posted: Sun Aug 02, 2020 11:57 am
by Sven
The copy&make approach is not necessarily "too expensive". You could certainly optimize your implementation by using 8 bitboards instead of 13, and also by using a slightly more efficient copying technique than a loop (e.g. memcpy() in C). But in general that approach can work equally well compared to the incremental make&unmake approach which needs additional logic for unmaking a move and also at least a small amount of additional data like the type of the captured piece. Most important from my view would be that you provide enough abstraction at the interface of your board class to be able to switch easily from one implementation to another.

Re: Simplifying code

Posted: Sun Aug 02, 2020 12:08 pm
by Henk
Yes c# also has a Clone and a CopyTo operation.
So I better use: private ChessBoardBits Copy() => new ChessBoardBits((UInt64[])pieces.Clone(), Other);


Advantage of the 13 bitboards is that you don't need to update whitepieces bitboard.

pieces[(int)pieceSort] ^= move.BitBoardValue;

You can make number of bytes much smaller by bitpacking or using encoder/decoder techniques .

Easiest is about 64* 3 bits.

Re: Simplifying code

Posted: Sun Aug 02, 2020 12:40 pm
by Henk
Looks like C# CopyTo is faster than Clone

Code: Select all

 
   private ChessBoardBits Copy()
        { 
            var piecesCopy = new UInt64[(int)none];
            pieces.CopyTo(piecesCopy, 0);        
            return new ChessBoardBits(piecesCopy, Other);
        }

Re: Simplifying code

Posted: Sun Aug 02, 2020 1:41 pm
by Pio
Henk wrote: Sun Aug 02, 2020 12:40 pm Looks like C# CopyTo is faster than Clone

Code: Select all

 
   private ChessBoardBits Copy()
        { 
            var piecesCopy = new UInt64[(int)none];
            pieces.CopyTo(piecesCopy, 0);        
            return new ChessBoardBits(piecesCopy, Other);
        }
Why not use structs?

Re: Simplifying code

Posted: Sun Aug 02, 2020 2:18 pm
by Henk
Pio wrote: Sun Aug 02, 2020 1:41 pm
Henk wrote: Sun Aug 02, 2020 12:40 pm Looks like C# CopyTo is faster than Clone

Code: Select all

 
   private ChessBoardBits Copy()
        { 
            var piecesCopy = new UInt64[(int)none];
            pieces.CopyTo(piecesCopy, 0);        
            return new ChessBoardBits(piecesCopy, Other);
        }
Why not use structs?
Yes that's an idea. I should try that. Hardly ever used structs by the way in C#. Already forgot they existed.