David Krauth avatar David Krauth committed 7f1d758

Incremental update

Comments (0)

Files changed (11)

+syntax: glob
+
+*~
+*.pyc
+local_settings.py
 WHITE_OR_BLACK = WHITE | BLACK
     
 # Cached look-up values for computing piece movement
-KNIGHT_VECTORS = (
-    ( 2, 1), 
-    ( 2,-1), 
-    (-2, 1), 
-    (-2,-1), 
-    ( 1, 2), 
-    ( 1,-2), 
-    (-1, 2), 
-    (-1,-2)
-)
 DIAGONAL_VECTORS   = ((1,1), ( 1,-1), (-1, 1), (-1,-1))
 ORTHOGONAL_VECTORS = ((1,0), (-1, 0), ( 0, 1), ( 0,-1))
 
 NO_CHECK  = 0x00
 CHECK     = 0x01
 CHECKMATE = 0x02
+CHECK_TOKENS = { 1: '+', 2: '#'}
 
 CASTLE_WK  = 0x01
 CASTLE_WQ  = 0x02
 #===============================================================================
 class Move:
 
-    CHECK_TOKEN    = {'#' : CHECKMATE, '+' : CHECK }
+    CHECK_TOKENS   = '#+'
+    CHECK_TOKEN    = {'#' : CHECKMATE, '+' : CHECK, '++' : CHECKMATE }
     PROMOTE_TOKEN  = 'QRNB'
     PIECE_TOKEN    = 'QRNBK'
-    KCASTLE_TOKEN  = 'O-O'
-    QCASTLE_TOKEN  = 'O-O-O'
+    KCASTLE_TOKEN  = ('O-O', 'o-o')
+    QCASTLE_TOKEN  = ('O-O-O', 'o-o-o')
     RESULT_TOKEN   = '*01'
     CAPTURE_TOKEN  = 'x'
     ANNOTATE_TOKEN = '!?'
+    
+    alt_notation_re = re.compile(r'^([a-h][1-8])[x-]?([a-h][1-8])$')
 
     #---------------------------------------------------------------------------
-    def __init__(self, color, counter, notation):
+    def __init__(self, board, notation):
+        self.board       = board
+        self.color       = board.color
+        self.counter     = board.counter
+        self.notation    = notation
+        self.capture     = False
+        self.checkState  = NO_CHECK
+        
         self.piece       = \
         self.castle      = \
         self.result      = \
         self.source      = \
         self.promote     = \
         self.destination = None
+        
+        self.parse()
 
-        self.color       = color
-        self.counter     = counter
-        self.notation    = notation
-        self.capture     = False
-        self.checkState  = NO_CHECK
+    #---------------------------------------------------------------------------
+    def parse(self):
+        notation = self.notation.rstrip(self.ANNOTATE_TOKEN)
+        if notation[0] in self.RESULT_TOKEN:
+            self.result = notation
+            return
 
-        #-----------------------------------------------------------------------
-        def parse():
-            notation = self.notation.rstrip(self.ANNOTATE_TOKEN)
+        check_tokens = ''
+        while notation[-1] in self.CHECK_TOKENS:
+            check_tokens += notation[-1]
+            notation = notation[:-1]
 
-            if notation[0] in self.RESULT_TOKEN:
-                self.result = notation
-                return
+        if check_tokens:
+            self.checkState = self.CHECK_TOKEN[check_tokens]
 
-            if notation[-1] in self.CHECK_TOKEN:
-                notation, self.checkState = (
-                    notation[:-1], 
-                    self.CHECK_TOKEN[notation[-1]]
-                )
+        if notation.startswith(self.KCASTLE_TOKEN):
+            self.castle = 2 if notation in self.QCASTLE_TOKEN else 6
+            return
+            
+        m = self.alt_notation_re.match(notation)
+        if m:
+            self.get_canonical_notation(
+                get_position(m.group(1)), 
+                get_position(m.group(2))
+            )
+            return
+         
+        if notation[0] in self.PIECE_TOKEN:
+            notation, self.piece = notation[1:], notation[:1]
 
-            if notation.startswith(self.KCASTLE_TOKEN):
-                self.castle = notation == self.QCASTLE_TOKEN and 2 or 6
-                return 
+        else:
+            if notation[-1] in self.PROMOTE_TOKEN:
+                notation, self.promote = notation[:-1], notation[-1:]
+                if notation[-1] == '=':
+                    notation = notation[:-1]
 
-            if notation[0] in self.PIECE_TOKEN:
-                notation, self.piece = notation[1:], notation[:1]
+        notation, self.destination = notation[:-2], notation[-2:]
+        if not notation:
+            return
 
-            else:
-                if notation[-1] in self.PROMOTE_TOKEN:
-                    notation, self.promote = notation[:-1], notation[-1:]
-                    if notation[-1] == '=':
-                        notation = notation[:-1]
+        if notation[-1] == self.CAPTURE_TOKEN:
+            notation, self.capture = notation[:-1], True
 
-            notation, self.destination = notation[:-2], notation[-2:]
+        elif notation[-1] == '-':
+            notation = notation[:-1]
 
-            if not notation:
-                return
+        self.source = notation
 
-            if notation[-1] == self.CAPTURE_TOKEN:
-                notation, self.capture = notation[:-1], True
+    #---------------------------------------------------------------------------
+    def get_canonical_notation(self, src, dest):
+        '''
+        Convert source/destination notation to algebriac format.
+        '''
+        self.piece = self.board.get_piece(src.rank, src.file)
+        if not is_piece(self.piece):
+            raise ChessMoveError, 'No piece at position %s' % (src,)
 
-            elif notation[-1] == '-':
-                notation = notation[:-1]
+        if self.piece.color != self.color:
+            raise ChessMoveError, 'Illegal move: Moving piece out of turn.'
+            
+        self.destination = dest
+        capture = not self.board.is_empty(dest.rank, dest.file)
+        position = self.piece.position
 
-            self.source = notation
+        # check to see if the piece is pawn     
+        if isinstance(self.piece, Pawn):
+            san = (capture and str(position)[0]) or ''
 
-        parse()
+        else:
+            san = self.piece.symbol
+    
+            # get all similar pieces
+            others = self.board.get_pieces(
+                self.piece.color, 
+                self.piece.__class__
+            )
+            
+            # remove our piece
+            others.remove(self.piece)
+            
+            # reduce to remaining pieces that could potentially move here
+            others = [n for n in others if dest in n.all_possible_moves()]
+            if others:
+
+                # first, check for no piece matching our column         
+                if not [o for o in others if o.position.file == position.file]:
+                    san += str(position)[0]
+
+                # second, check for no piece matching our rank
+                elif not [o for o in others if o.position.rank == position.rank]:
+                    san += str(position)[1]
+
+                # otherwise, just use the whole position notation
+                else:
+                    san += str(position)
+
+        if capture:
+            san += 'x'
+
+        san += str(dest)
+        if self.checkState:
+            san += CHECK_TOKENS[self.checkState]
+            
+        self.notation = san
 
     #---------------------------------------------------------------------------
     def __repr__(self):
-        return 'Move(%d, %d, "%s")' % (self.color, self.counter, self.notation)
+        return 'Move(color=%d, counter=%d, notation="%s")' % (
+            self.color, 
+            self.counter, 
+            self.notation
+        )
 
     #---------------------------------------------------------------------------
     def __str__(self):
     @property
     def is_white(self):
         return self.color == WHITE
+        
     #---------------------------------------------------------------------------
     @property
     def index(self):
         possibleMoves = self.all_possible_moves()
         if position not in possibleMoves:
             raise ChessError(
-                'Invalid destination (%s) for %r. Valid moves: [%s]' % (
+                'Move %s: Invalid destination (%s) for %r. Valid moves: [%s]' % (
+                    self.board.counter,
                     position, 
                     self, 
                     ','.join([str(m) for m in possibleMoves])
         # finally, check for a promotion
         if self.position.rank in (0,7):
             if not self.board.game.move.promote:
-                raise ChessError, 'Unspecified promotion piece'
-            
-            self.__class__ = CHESS_PIECES[self.board.game.move.promote]
+                self.__class__ = CHESS_PIECES['Q']
+            else:
+                self.__class__ = CHESS_PIECES[self.board.game.move.promote]
             
         self.board.halfmoves = 0
 
 
     symbol = 'N'
     value  = 3
-    search_vectors = KNIGHT_VECTORS
     contiguousVector = False
+    search_vectors = (
+        ( 2, 1), 
+        ( 2,-1), 
+        (-2, 1), 
+        (-2,-1), 
+        ( 1, 2), 
+        ( 1,-2), 
+        (-1, 2), 
+        (-1,-2)
+    )
 
 
 #===============================================================================
                 continue
             
             vector = Vector(self)
-
             if item == EMPTY:
                 vector += get_position(rank, file)
-                
             else: 
-
                 if item.color == self.color:
                     # this is one of our pieces that we are defending: note it 
                     # and stop
                     vector.guards_piece(item)
-
                 else:
                     # found an enemy
                     vector += item.position
     return x | CASTLE_MAP.get(y,0)
 
 
-#===============================================================================
-# Chessboard
+
 #===============================================================================
 class Chessboard:
     '''Handles chess board behavior.'''
     def get_fen_placement_string(self):
         '''Generates a FEN placement string from the current position.'''
         ps = ''
-        files = [0,1,2,3,4,5,6,7]
-        for rank in [7,6,5,4,3,2,1,0]:
+        files = [0, 1, 2, 3, 4, 5, 6, 7]
+        for rank in [7, 6, 5, 4, 3, 2, 1, 0]:
             if ps:
                 ps += '/'
                 
     Chess game interfaces
     '''
 
-    alt_notation_re = re.compile('^([a-h][1-8])-?([a-h][1-8])$')
-
     #---------------------------------------------------------------------------
     def __init__(self, pgn):
         self.pgn = pgn
         self.result = '*' #self.pgn.GetResult()
         self.board = Chessboard(self, self.fen[0])
         self.move = None
+        self.moves = []
         self.canContinue = True
 
     #---------------------------------------------------------------------------
             return self.fen[-1]
 
     #---------------------------------------------------------------------------
-    def get_canonical_notation(self, src, dest):
-        '''Convert source/destination notation to algebriac format.'''
-        self.move.piece = self.board.get_piece(src.rank, src.file)
-        if self.move.piece.color != self.move.color:
-            raise ChessMoveError, 'Illegal move: Moving piece out of turn.'
-            
-        self.move.destination = dest
-        
-        if not is_piece(self.move.piece):
-            raise ChessMoveError, 'No piece at position %s' % (src,)
-            
-        capture = not self.board.is_empty(dest.rank, dest.file)
-        position = self.move.piece.position
-
-        # check to see if the piece is pawn     
-        if isinstance(self.move.piece, Pawn):
-            san = (capture and str(position)[0]) or ''
-
-        else:
-            san = self.move.piece.symbol
-    
-            # get all similar pieces
-            others = self.board.get_pieces(
-                self.move.piece.color, 
-                self.move.piece.__class__
-            )
-            
-            # remove our piece
-            others.remove(self.move.piece)
-            
-            # reduce to remaining pieces that could potentially move here
-            others = [n for n in others if dest in n.all_possible_moves()]
-            
-            if others:
-
-                # first, check for no piece matching our column         
-                if not [o for o in others if o.position.file == position.file]:
-                    san += str(position)[0]
-
-                # second, check for no piece matching our rank
-                elif not [o for o in others if o.position.rank == position.rank]:
-                    san += str(position)[1]
-
-                # otherwise, just use the whole position notation
-                else:
-                    san += str(position)
-
-        if capture:
-            san += 'x'
-
-        san += str(dest)
-        self.move.notation = san
-
-    #---------------------------------------------------------------------------
     def eval_san_notation(self):
         if isinstance(self.move.piece, Chesspiece):
             return
             )
 
         try:
-            self.move = Move(self.board.color, self.board.counter, notation)
+            self.move = Move(self.board, notation)
             if self.move.result:
                 raise GameComplete(self.move.result)
 
-            m = self.alt_notation_re.match(notation)
-            if m:
-                self.get_canonical_notation(
-                    get_position(m.group(1)), 
-                    get_position(m.group(2))
-                )
-            
         except GameComplete, result:
             self.result = str(result)
             return None
 
             else:
                 self.eval_san_notation()
-
                 self.move.origin = self.move.piece.position
                 self.move.piece.move_to(self.move.destination)
 
                 if self.move_exposes_check(self.board.get_king(self.move.color)):
                     raise ChessMoveError, 'Move results with king in check'
             
-
-        except: # ChessError, why:
-            tp, ins, tb = sys.exc_info()
-            ins.move = self.move
-            self.undo_move()
-            raise
+        except ChessError, why:
+            print self.board
+            if isinstance(why, ChessError):
+                self.undo_move()
+                why.args += (' [%r]' % (self.move, ),)
+                raise
+            else:
+                typ, val, tb = sys.exc_info()
+                raise ChessError('[%r] %s' % (self.move, val))
+            
                 
         king = self.board.get_king(opposite_color(self.move.color))
         checkState = king.get_check_state()
         self.move.result = self.result
         self.move.fen = self.board.get_fen_notation()
         self.fen.append(self.move.fen)
+        self.moves.append(self.move)
         return self.move
 
     #---------------------------------------------------------------------------
             if cb and callable(cb):
                 cb(move)
 
+
 #-------------------------------------------------------------------------------
 def play_game(history):
     game = Game(Pgn.Read(history))
         
     print 
     print game
+    linelen = 0
+    for move in game.moves:
+        notation = move.notation
+        s = '%s. %s' % (move.counter, notation) if move.is_white else notation
+        print s,
+        linelen += len(s) + 1
+        if linelen >= 44:
+            linelen = 0
+            print
+            
+    print
 
 
 ################################################################################
     import sys
     parser = pgn_parser(open(sys.argv[1]))
     for i, game in enumerate(parser):
-        print '%d %s' % (i + 1, '-' * 50)
+        print 'Game %d %s' % (i + 1, '-' * 50)
         print game
         print
+from django import forms
+from echess import models as echess
+
+COLOR_CHOICES = (
+    ('opp_choice', "Opponent's Choice"),
+    ('white', 'White'),
+    ('black', 'Black'),
+    ('next', 'Next in Series'),
+)
+
+#===============================================================================
+class NewGameForm(forms.Form):
+
+    opponent = forms.ModelChoiceField(
+        echess.ChessProfile.objects.none(),
+        empty_label='First Available',
+        required=False
+    )
+    color = forms.ChoiceField(choices=COLOR_CHOICES)
+    
+    #---------------------------------------------------------------------------
+    def __init__(self, user, *args, **kws):
+        super(NewGameForm, self).__init__(*args, **kws)
+        self.fields['opponent'].queryset = echess.ChessProfile.objects.exclude(user=user)

media/css/chess2.css

 #board { border: 1px solid #777; padding: 0; width: 320px; float: left; background: url(/media/chess/img/board-bg4.png) repeat; }
 #board div { width: 36px; height: 36px; margin: 0; padding: 0; float: left; }
 .white { border: 2px solid transparent; }
-.black { border: 2px solid transparent; }
+.black { border: 2px solid transparent; background-color: #ccc; }
 .red { border: 2px solid #800; }
 .green { border: 2px solid #080; }
 #rows { float: left; margin: 0; }
 #rows div { height: 30px; width: 1em; padding: 10px 0 0 0; font-size: smaller; font-weight: bold; }
 #cols { clear: both; margin-left: 1em; }
 #cols div { float: left; width: 40px; text-align: center; font-size: smaller; font-weight: bold; }
+
+fieldset table { border: none; }
+fieldset tbody th {text-align: right; }
+fieldset tfoot td { text-align: right; }
 
 
 #===============================================================================
+class ChessProfile(models.Model):
+    user = models.ForeignKey(User)
+    rating = models.IntegerField(default=0)
+    rating_count = models.IntegerField(default=0)
+    
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return self.user.username
+    
+
+#===============================================================================
 class Game(models.Model):
 
     RESULT_OPTIONS = (
     result = models.CharField(max_length=1, choices=RESULT_OPTIONS, default='U')
     
     pgn = models.TextField(blank=True)
-    white = models.ForeignKey(User, related_name='game_white_set')
-    black = models.ForeignKey(User, related_name='game_black_set')
+    white = models.ForeignKey(ChessProfile, related_name='game_white_set')
+    black = models.ForeignKey(ChessProfile, related_name='game_black_set')
 
     #===========================================================================
     class Meta:

templates/echess/base.html

     <!-- Import fancy-type plugin. -->
     <link rel="stylesheet" href="/media/css/blueprint/plugins/fancy-type/screen.css" type="text/css" media="screen, projection" />
     <style type="text/css">
-        /*@import url("/media/chess/css/layout.css");*/
+        @import url("/media/chess/css/chess2.css");
         h1 { margin-top: 1em; font-size: 2em; }
         h1 a img { vertical-align: middle; }
         table { border-collapse: collapse; border: 1px solid #c3d9ff; }
-        td, th { border: 1px solid #c3d9ff; }
-        tr.odd td { background-color: ;#e5ecf9; }
+        tr.odd td { background-color: #e5ecf9; }
         a { text-decoration: none; }
     </style> 
     {% block extra_head %}{% endblock extra_head %}
                 <div class="box quiet">
                     <ul>
                         {% if not user.is_authenticated %}
-                        <li><a href="#">Sign up!</a></li>{% endif %}
-                        <li><a href="#">New Match Game</a></li>
-                        <li><a href="#">New Practice Game</a></li>
-                        <li><a href="{% url chess-openings %}">Library of Openings</a></li>
+                        <li>
+                            <a href="{% url acct-login %}">Login</a> &mdash; <strong>or</strong> 
+                            <a href="{% url registration-register %}">Sign up!</a>
+                        </li>
+                        {% else %}
+                        <li>
+                            <strong>Hello, {{ user }}</strong> &mdash; 
+                            <a href="{% url acct-logout %}">Logout</a>
+                        </li>
+                        <li><a href="{% url chess-game-new %}">New Game</a></li>
+                        {% endif %}
+                        <li><a href="#">Practice Game</a></li>
+                        <li><a href="{% url chess-openings %}">Opening Library</a></li>
                         <li><a href="#">About</a></li>
                         
                     </ul>
             </div>
 
              <div class="prepend-top push-3 span-8 last">
-                <span class="quiet">Copyright &copy; 2009 David Krauth v3.0.0</span>
+                <span class="quiet">Copyright &copy; 2010 David Krauth v3</span>
             </div>
         </div>
     </div>

templates/echess/index.html

             {% for game in games %}
             <tr class="{% cycle 'odd' 'even' %}">
                 <td><a href="{{ game.get_absolute_url }}">View</a></td>
-                <td>{{ game.white.get_full_name }}</td>
-                <td>{{ game.black.get_full_name }}</td>
+                <td>{{ game.white }}</td>
+                <td>{{ game.black }}</td>
                 <td>{{ game.get_result_display }}</td>
                 <td>{{ game.start_time|date:"Y.m.d" }}</td>
             </tr>

templates/echess/new_game.html

+{% extends "echess/base.html" %}
+{% block extra_head %}{% endblock extra_head %}
+{% block main_content %}
+    <form action="" method="post" accept-charset="utf-8">
+    <fieldset>
+        <legend>New Game</legend>
+        <table>
+            <tbody>
+            {{ form }}
+            </tbody>
+            <tfoot>
+            <tr>
+                <td colspan="2">
+                    <input type="submit" value="Continue &rarr;">                        
+                </td>
+            </tr>
+            </tfoot>
+        </table>
+    </fieldset>
+    </form>
+{% endblock %}
 urlpatterns = patterns('',
     url(r'^$', views.index, name='chess-index'),
     url(r'^games/$', views.games, name='chess-games'),
+    url(r'^games/new/$', views.new_game, name='chess-game-new'),
     url(r'^games/(\d+)/$', views.game, name='chess-game'),
     url(r'^test/$', views.test, name='chess-test'),
     url(r'^play/$', views.play, name='chess-playback'),
+from django import http
 from django.contrib.auth.models import User
 from django.shortcuts import render_to_response, get_object_or_404
 
+from jargon.shortcuts import request_to_response
 from echess.models import *
+from echess import forms
 import chesslib
 
 #-------------------------------------------------------------------------------
             
         else:
             self.page.FormatResult(result)
+
     
 #-------------------------------------------------------------------------------
 def MatchMove(self):
 #-------------------------------------------------------------------------------
 def index(request):
     games = Game.objects.all()
-    return render_to_response('echess/index.html', dict(games=games))
+    return request_to_response(request, 'echess/index.html', dict(games=games))
 
 
 #-------------------------------------------------------------------------------
     moves = []
     game = chesslib.Game(pgn)
     game.play(moves.append)
-    return render_to_response(
+    return request_to_response(
+        request,
         'echess/game.html', 
         dict(moves=moves, game=game)
     )
 #-------------------------------------------------------------------------------
 def games(request):
     games = Game.objects.all()
-    
-    return render_to_response('echess/game.html', dict(games=games))
+    return request_to_response(request, 'echess/game.html', dict(games=games))
+
 
 #-------------------------------------------------------------------------------
 def test(request):
-    return render_to_response('echess/test.html')
+    return request_to_response(request, 'echess/test.html')
 
 
 #-------------------------------------------------------------------------------
     pgn = chesslib.Pgn.parse(request.POST['MoveText'].encode('utf8'))
     game = chesslib.Game(pgn)
     game.play(moves.append)
-    print(game.fen)
-    return render_to_response(
+    # print(game.fen)
+    return request_to_response(
+        request,
         'echess/game.html', 
         dict(moves=moves, game=game)
     )
     if eco:
         o = o.filter(eco=eco)
     
-    return render_to_response(
+    return request_to_response(
+        request,
         'echess/openings.html', 
         dict(openings=o)
     )
     game = chesslib.Game(pgn)
     game.play(moves.append)
     
-    return render_to_response(
+    return request_to_response(
+        request,
         'echess/game.html', 
         dict(moves=moves, game=game, opening=o)
     )
 
+
+#-------------------------------------------------------------------------------
+def new_game(request):
+    if request.method == 'POST':
+        form = forms.NewGameForm(request.user, request.POST)
+        if form.is_valid():
+            return http.HttpResponseRedirect(request.path)
+    else:
+        form = forms.NewGameForm(request.user)
+        
+    return request_to_response(request, 'echess/new_game.html', dict(form=form))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.