From: david
Subject: function to translate chess positions
Date: 
Message-ID: <fd76c813-a0b9-41ea-9332-0194cda76e2c@m42g2000yqb.googlegroups.com>
hello again lispers,
i thought to write a little program to generate some simple endgame
positions in fen format.
first i need to get a list of the row and column. this is what i have
come up with:

(defun get-position ()
  (+ 1 (random 64)))

(defun break-down-position (x)
  (multiple-value-list (floor x 8)))

(defun fix-position (x)
  (mapcar #'1+ (break-down-position x)))

(defun test()
  (dotimes (i 64)
    (print (fix-position i))))

i think my code is doing what i want. but i am only an egg.
i'm sure there is a better way.
please to review my code.

thanks, david

From: Pascal J. Bourguignon
Subject: Re: function to translate chess positions
Date: 
Message-ID: <7c63j94c4l.fsf@pbourguignon.anevia.com>
david <······@gmail.com> writes:

> hello again lispers,
> i thought to write a little program to generate some simple endgame
> positions in fen format.
> first i need to get a list of the row and column. this is what i have
> come up with:
>
> (defun get-position ()
>   (+ 1 (random 64)))

If you add one here, (and I would rather use 1+),

> (defun break-down-position (x)
>   (multiple-value-list (floor x 8)))

Then you shoud remove it here.  (And I would rather use TRUNCATE)

But why would you want to keep the position with an offset,
internally?


> (defun fix-position (x)
>   (mapcar #'1+ (break-down-position x)))

Why make it a separate function?


> (defun test()
>   (dotimes (i 64)
>     (print (fix-position i))))
>
> i think my code is doing what i want. but i am only an egg.
> i'm sure there is a better way.
> please to review my code.


(defun random-position () (1+ (random 64)))
(defun row-column (position) (mapcar (function 1+) (multiple-value-list (truncate (1- position) 8))))

(loop :for position :from 1 :to 64 :do (print (row-column position)))

(1 1) 
(1 2) 
...
(8 7) 
(8 8) 


Even if lisp typing is dynamic, you should still keep trace of what
type your values are.  If you have a function that gives position in
the range [1..64], and another that takes a position, then it should
also take it in the same range [1..64] instead of [0..63].

One way to ensure you don't make a mistake is to use objects:



(deftype row    () '(integer 0 7))
(deftype column () '(integer 0 7))

(defclass cbpos ()
   ((column :initarg :column :initform 0 :type column :accessor column)
    (row    :initarg :row    :initform 0 :type row    :accessor row)))

(defmethod print-object ((self cbpos) stream)
  (print-unreadable-object (self stream :type t)
      (format stream "~[A~;B~;C~;D~;E~;F~;G~;H~]~A" (column self) (row self))))

(defun cbpos (column row)
  (make-instance 'cbpos
     :column (etypecase column
               (integer column)
               ((or character string symbol) 
                  (or (position (string column) '("A" "B" "C" "D" "E" "F" "G" "H")
                                :test (function string-equal))
                      (error "Column must be one of the letters from A to H, not ~S" column))))
     :row row))


(defmethod fen ((self cbpos)) (+ 1 (* 8 (row self)) (column self))) ; or the other way I don't know.

(defmethod initialize-instance ((self cbpos) &rest arguments &key (fen nil fenp) &allow-other-keys)
   (call-next-method)
   (when fenp
      (multiple-value-bind (r c) (truncate (1- fen) 8) ; or the other way I don't know.
        (setf (column self) c
              (row    self) r)))
   self)

(defun random-cbpos () (cbpos (random 8) (random 8)))


(cbpos 'a 2) --> #<CBPOS A2>
(cbpos  0 2) --> #<CBPOS A2>
(random-cbpos) -> #<CBPOS D2>
(random-cbpos) -> #<CBPOS H2>

(loop :for fen :from 1 :to 64 
      :for pos = (make-instance 'cbpos :fen fen)
      :do (print  (list pos (row pos) (column pos) (fen pos))))

(#<CBPOS A0> 0 0 1) 
(#<CBPOS B0> 0 1 2) 
...
(#<CBPOS G7> 7 6 63) 
(#<CBPOS H7> 7 7 64) 

-- 
__Pascal Bourguignon__
From: david
Subject: Re: function to translate chess positions
Date: 
Message-ID: <fa2262a8-7fc7-4add-a90f-ae3ebdd1669f@e18g2000yqo.googlegroups.com>
On Feb 17, 3:59 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> (#<CBPOS H7> 7 7 64)
>
> --
> __Pascal Bourguignon__

i think i see why your version is better. mine was wrong anyway
because i was using
dotimes wrong. better to focus on loop than dotimes and do?
i looked at the spec for truncate. i assume it is better because i
know i will have a positive input. and 1+ is better because i'm just
incrementing by 1.
correct?
i was hoping to avoid clos until i learn the rest of cl a little
better.
good idea?

here is new improved code:

(defun random-position () (1+ (random 64)))

(defun rank-file (position) (mapcar (function 1+) (multiple-value-list
(truncate (1- position) 8))))

;(loop :for position :from 1 :to 64 :do (print (row-column position)))


(defun make-file-list (p x)
	  (cond ((= x 1)(print (list p 7)))
		((= x 8)(print (list 7 p)))
		(t (print (list (1- x) p (- 8 x ))))))


(loop for n from 1 to 8 do
	      (make-file-list 'b n))


now i need to build a list of file lists and eventually figure out how
to translate to fen
format which looks like this:
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
(for the starting position)
suggestions welcome
thanks, david
From: Pascal J. Bourguignon
Subject: Re: function to translate chess positions
Date: 
Message-ID: <7cwsbp2g9f.fsf@pbourguignon.anevia.com>
david <······@gmail.com> writes:

> On Feb 17, 3:59 am, ····@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> (#<CBPOS H7> 7 7 64)
>>
>> --
>> __Pascal Bourguignon__
>
> i think i see why your version is better. mine was wrong anyway
> because i was using
> dotimes wrong. better to focus on loop than dotimes and do?

DOTIMES and DOLIST are ok, when you only need a simple loop.

DO is correct, but harder to read, since it has a lot of elements
distinguished only by their position in the syntactic tree
(ie. parentheses).  In general, LOOP is more readable than DO.

For complex loops, (eg. when you must loop thru two sequences in
parallel, or when you have to mix in several stop conditions, etc),
LOOP might be better, but its style is not too lispy. It's kind of a
loop specific DSL.  You may like better ITERATE (and/or SERIES).
http://www.cliki.net/iterate 
http://www.cliki.net/Series

> i looked at the spec for truncate. i assume it is better because i
> know i will have a positive input.

Well I'm not so sure TRUNCATE is better than FLOOR after all.  I guess
I'm influenced by the names of libraries in other programming
languages.

One thing is if you want to use modulo arithmetic, in which case FLOOR
would be better.
  -1 ≡ 7 [8]
(floor    -1 8) --> 7 
(truncate -1 8) --> -1

On the other hand, it seems on some processors TRUNCATE may be (or
have been) implemented more efficiently.  


But in  your case it won't matter either way since your numbers are
positive, and your module is a power of 2, so it could even be
optimized into LDB operations.

(values (ldb (byte 3 0) (1- fen))
        (ldb (byte 3 3) (1- fen))) === (floor (1- fen) 8)



> and 1+ is better because i'm just
> incrementing by 1.
> correct?

Yes.  (But in honest implementations, both (1+ x) and (+ 1 x) should
generate the same code).


> i was hoping to avoid clos until i learn the rest of cl a little
> better.
> good idea?

It is possible.  I don't know if it's a good idea.

After all, in CLOS, the methods are not attached to objects, but to
generic functions, that is, you can  use methods without using CLOS
objects, in a purely "functionnal" way, on one hand, and on the other
hand, the complexities of CLOS are actually in the MOP (Meta Object
Protocol), when using DEFCLASS you just have a sophisticated DEFSTRUCT
with multiple inheritance.

My opinion is that you can use 20% of CLOS for 80% of OO effect
without much difficulty, if you already know some OOP.



> here is new improved code:
> (defun make-file-list (p x)
> 	  (cond ((= x 1)(print (list p 7)))
> 		((= x 8)(print (list 7 p)))
> 		(t (print (list (1- x) p (- 8 x ))))))
>
>
> (loop for n from 1 to 8 do
> 	      (make-file-list 'b n))

In general, you can avoid putting I/O inside functions.  You can still
try them out at the REPL, and count on the rePl to print the results.
In the case of numerical cases you can use CASE:

 (defun make-file-list (p x)
   (case x
     ((1) (list p 7))
     ((8) (list 7 p))
     (otherwise (list (1- x) p (- 8 x)))))


C/USER[67]> (MAKE-FILE-LIST '|b| 2)
(1 |b| 6)
C/USER[68]> (MAKE-FILE-LIST 'R 1)
(R 7)
C/USER[69]> (loop for i from 1 to 8 collect (make-file-list 'x i))
((X 7) (1 X 6) (2 X 5) (3 X 4) (4 X 3) (5 X 2) (6 X 1) (7 X))


-- 
__Pascal Bourguignon__
From: david
Subject: Re: function to translate chess positions
Date: 
Message-ID: <6fdcfcdc-19a1-49fd-8c31-71ab9bc7387a@v42g2000yqj.googlegroups.com>
On Feb 17, 10:13 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> C/USER[68]> (MAKE-FILE-LIST 'R 1)
> (R 7)
> C/USER[69]> (loop for i from 1 to 8 collect (make-file-list 'x i))
> ((X 7) (1 X 6) (2 X 5) (3 X 4) (4 X 3) (5 X 2) (6 X 1) (7 X))
>
> --
> __Pascal Bourguignon__

here is my code so far now. i would like to find a better way
than all the calls to setf. make-file-list only works if there
is only one piece on that rank. but i will fix that later.
thanks so much for help.
must sleep now.

(defun random-position () (1+ (random 64)))

(defun get-positions ()
  (loop
     :with results = '()
     :for alea = (random-position)
     :while (< (length results) 4)
     :do (pushnew alea results)
     :finally (return results)))

(defun rank-file (position)
  (mapcar (function 1+) (multiple-value-list (truncate (1- position)
8))))



(defparameter fenlist (make-list 8 :initial-element '(8)))

(defun make-file-list (p x)
   (case x
     ((1) (list p 7))
     ((8) (list 7 p))
     (otherwise (list (1- x) p (- 8 x)))))

(defun make-fen (rank file p)
  (setf (nth (1- rank) fenlist) (make-file-list p file)))


(setf lst (get-positions))
(setf wk (rank-file (first lst)))
(setf wb (rank-file (second lst)))
(setf wn (rank-file (third lst)))
(setf bk (rank-file (fourth lst)))