»

search

Dom λ Blog

 Just another WordPress site

  • Categories

    • Programming
      • Common Lisp
      • Javascript
    • Softwares
  • Me on the web

    • Facebook
    • Twitter
    • Website
  • Meta

    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
  • Porting a snake game from pascal to javascript

    While diving inside my old files I found a snake game I wrote in pascal when I was sixteen. Taken by a wave of nostalgia I decided to port it in javascript to make it work in a webpage. See here.

  • A scopa game in Common Lisp

    This is a scopa game for 2 players, human vs pc, written in Common Lisp mostly for fun, and to put in practice what I learned about CL so far.

    This is my first “real program” (if it can be called so) in Common Lisp, so just don’t expect it to be well-designed. Comments are very welcome.. and don’t strive to be polite about it. :-)

    Source code

    Use (play) to play.

    Example session at the REPL of GNU CLISP:
    I lost against the PC :/

    [1]> (load "scopa.clisp")
    ;; Loading file scopa.clisp ...
    ;; Loaded file scopa.clisp
    T
    [2]> (play)
    
    On the table:    [7 OF COPPE],   [6 OF DENARI],   [2 OF DENARI],    [8 OF COPPE]
    Your hand:     [5 OF BASTONI],  [9 OF BASTONI],  [2 OF BASTONI]
    Stock: 30 cards.
    
    It's PC turn.
    
    Player PC plays [7 OF SPADE] taking [7 OF COPPE]
    
    On the table:   [6 OF DENARI],   [2 OF DENARI],    [8 OF COPPE]
    Your hand:     [5 OF BASTONI],  [9 OF BASTONI],  [2 OF BASTONI]
    Stock: 30 cards.
    
    It's HUMAN turn.
    
    1) Play [2 OF BASTONI] taking [2 OF DENARI]
    2) Play [5 OF BASTONI]
    3) Play [9 OF BASTONI]
    What do you want to do?
    1
    Player HUMAN plays [2 OF BASTONI] taking [2 OF DENARI]
    
    On the table:   [6 OF DENARI],    [8 OF COPPE]
    Your hand:     [5 OF BASTONI],  [9 OF BASTONI]
    Stock: 30 cards.
    
    It's PC turn.
    
    Player PC plays [10 OF COPPE]
    
    On the table:   [6 OF DENARI],    [8 OF COPPE],   [10 OF COPPE]
    Your hand:     [5 OF BASTONI],  [9 OF BASTONI]
    Stock: 30 cards.
    
    It's HUMAN turn.
    
    1) Play [5 OF BASTONI]
    2) Play [9 OF BASTONI]
    What do you want to do?
    2
    Player HUMAN plays [9 OF BASTONI]
    
    ...
    ...
    
    It's PC turn.
    
    Player PC plays [4 OF DENARI]
    
    On the table:    [8 OF SPADE],   [4 OF DENARI]
    Your hand:       [2 OF COPPE]
    Stock: 0 cards.
    
    It's HUMAN turn.
    
    1) Play [2 OF COPPE]
    What do you want to do?
    1
    Player HUMAN plays [2 OF COPPE]
    Player HUMAN collect all the remaining cards on the table.
    
    SCORE ASSIGNMENTS
    Greatest number of cards: PC
    Greatest number of denari cards: PC
    Settebello: PC
    Prime: PC
    HUMAN
    0 + 0 + 0 scopas = 0
    -------
    PC
    0 + 4 + 0 scopas = 4
    
    On the table:    [5 OF SPADE],   [6 OF DENARI],  [3 OF BASTONI],    [3 OF SPADE]
    Your hand:     [5 OF BASTONI],    [7 OF SPADE],   [10 OF SPADE]
    Stock: 30 cards.
    
    It's HUMAN turn.
    
    1) Play [5 OF BASTONI] taking [5 OF SPADE]
    2) Play [10 OF SPADE]
    3) Play [7 OF SPADE]
    What do you want to do?
    1
    Player HUMAN plays [5 OF BASTONI] taking [5 OF SPADE]
    
    ...
    ...
    
    It's PC turn.
    
    Player PC plays [5 OF DENARI]
    Player PC collect all the remaining cards on the table.
    
    SCORE ASSIGNMENTS
    Greatest number of cards: PC
    Greatest number of denari cards: PC
    Settebello: HUMAN
    Prime: PC
    HUMAN
    0 + 1 + 0 scopas = 1
    -------
    PC
    4 + 3 + 2 scopas = 9
    
    On the table:   [4 OF DENARI],    [5 OF SPADE],   [9 OF DENARI],   [10 OF SPADE]
    Your hand:     [7 OF BASTONI],    [8 OF SPADE],   [1 OF DENARI]
    Stock: 30 cards.
    
    It's HUMAN turn.
    
    1) Play [8 OF SPADE]
    2) Play [1 OF DENARI]
    3) Play [7 OF BASTONI]
    What do you want to do?
    1
    Player HUMAN plays [8 OF SPADE]
    
    ...
    ...
    
    It's PC turn.
    
    Player PC plays [6 OF SPADE] taking [2 OF SPADE], [4 OF BASTONI]
    Player PC collect all the remaining cards on the table.
    
    SCORE ASSIGNMENTS
    Greatest number of cards: PC
    Greatest number of denari cards: PC
    Settebello: PC
    Prime: HUMAN
    HUMAN
    1 + 1 + 1 scopa = 3
    -------
    PC
    9 + 3 + 2 scopas = 14
    The game is over!
    The winner is PC with a score of 14!
    NIL
    [3]>
    

    Entire session.

  • Lesson learned: never modify literals, quote lists only for constant data

    While writing a scopa card game in Common Lisp both as a pet project and a way to practice a little lisp, I had a hard time trying to understand what was going on here:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    (defun prime-points-of-card (card)
      (let ((prime-points '((7 21) (6 18) (1 16) (5 15) (4 14) (3 13) (2 12) (10 10) (9 10) (8 10))))
        (cadr (assoc (card-rank card) prime-points))))
     
    (defun prime-points (deck)
      (let ((scores '((denari 0) (coppe 0) (bastoni 0) (spade 0))))
        (loop for card across deck do
             (let ((suit (card-suit card)))
               (setf (cadr (assoc suit scores)) (max (cadr (assoc suit scores))
                                                     (prime-points-of-card card)))))
        (apply #'+ (mapcar #'cadr scores))))

    I was expecting prime-points to return the prime points of a sequence of cards, but it kept returning what seemed to me just meaningless numbers.

    I added a couple of prints to inspect what was going on:
    (yeah I still need to learn how to debug effectively with lisp)

    (defun prime-points (deck)
      (let ((scores '((denari 0) (coppe 0) (bastoni 0) (spade 0))))
        (print scores)
        (loop for card across deck do
             (let ((suit (card-suit card)))
               (setf (cadr (assoc suit scores)) (max (cadr (assoc suit scores))
                                                     (prime-points-of-card card)))))
        (print scores)
        (apply #'+ (mapcar #'cadr scores))))

    and this is what I found:

    [3]> (prime-points #())
     
    ((DENARI 0) (COPPE 0) (BASTONI 0) (SPADE 0))
    ((DENARI 0) (COPPE 0) (BASTONI 0) (SPADE 0))
    0
    [4]> (prime-points #())
     
    ((DENARI 0) (COPPE 0) (BASTONI 0) (SPADE 0))
    ((DENARI 0) (COPPE 0) (BASTONI 0) (SPADE 0))
    0
    [5]> (prime-points (deck 'stock))
     
    ((DENARI 0) (COPPE 0) (BASTONI 0) (SPADE 0))
    ((DENARI 21) (COPPE 21) (BASTONI 21) (SPADE 21))
    84
    [6]> (prime-points #())
     
    ((DENARI 21) (COPPE 21) (BASTONI 21) (SPADE 21))
    ((DENARI 21) (COPPE 21) (BASTONI 21) (SPADE 21))
    84

    scores was keeping its value at each call to prime-points!
    Wasn’t let supposed to create a new lexical scope?
    Yes, it is and in fact it is what let does.
    The problem is to understand that

    (quote (foo bar baz))

    and its shortcut

    '(foo bar baz)

    are just list literals the same way as

    #(1 2 3) ; Literal array
    "literal string"

    This means that

    (quote (foo bar baz))

    is not equivalent to

    (list 'foo 'bar 'baz)

    In the first case, the list is literal, in the second it is generated by code.
    When the list is literal, the compiler could copy it into write-protected memory or share it among more functions (or more calls to the same function) hence the consequences are undefined if it is modified.

    As HyperSpec’s QUOTE page states:

    Special Operator QUOTE

    Syntax:
    quote object => object

    Arguments and Values:
    object—an object; not evaluated.

    Description:
    The quote special operator just returns object.

    The consequences are undefined if literal objects (including quoted objects) are destructively modified.

    Ok. This may seem trivial for anybody else, but it was not for me.

    This is the same thing that happens in other languages.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    #include <stdio.h>
     
    void foo() {
      char string[] = "Fresh new at every call";
     
      printf("%s\n", string);
      string[0]++;
      printf("%s\n", string);
    }
     
    void bar() {
      char* string = "I could be in read-only memory";
     
      printf("%s\n", string);
      string[0]++; // Possible segmentation fault
      printf("%s\n", string);
    }
     
    int main(int argc, char **argv) {
      foo();
      foo();
      foo();
     
      bar();
      bar();
      bar();
     
      return 0;
    }

    This is what happens with gcc:

    dom@dom-laptop:~/Progetti/c$ gcc -o test test.c && ./test
    Fresh new at every call
    Gresh new at every call
    Fresh new at every call
    Gresh new at every call
    Fresh new at every call
    Gresh new at every call
    I could be in read-only memory
    Errore di segmentazione
    dom@dom-laptop:~/Progetti/c$

    So if you need to define a data structure the way I did, use list or just copy-tree the literal data:

    (defun prime-points (deck)
      (let ((scores (copy-tree '((denari 0) (coppe 0) (bastoni 0) (spade 0)))))
        (loop for card across deck do
             (let ((suit (card-suit card)))
               (setf (cadr (assoc suit scores)) (max (cadr (assoc suit scores))
                                                     (prime-points-of-card card)))))
        (apply #'+ (mapcar #'cadr scores))))

    I need to thank people at comp.lang.lisp for always being friendly and very didactic to lurk at.

  •  

Wordpress // Photon by Jacob Andreas