File indexing completed on 2021-12-21 12:50:22

0001 /*
0002  *  ksokoban - a Sokoban game by KDE
0003  *  Copyright (C) 1998  Anders Widell  <d95-awi@nada.kth.se>
0004  *
0005  *  This program is free software; you can redistribute it and/or modify
0006  *  it under the terms of the GNU General Public License as published by
0007  *  the Free Software Foundation; either version 2 of the License, or
0008  *  (at your option) any later version.
0009  *
0010  *  This program is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *  GNU General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU General Public License
0016  *  along with this program; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #include <assert.h>
0021 #include <string.h>
0022 #include <ctype.h>
0023 
0024 #include "Move.h"
0025 #include "LevelMap.h"
0026 
0027 Move::Move (int _startX, int _startY) {
0028   assert (_startX>=0 && _startX<=MAX_X && _startY>=0 && _startY<=MAX_Y);
0029 
0030   moves_ = new unsigned short[400];
0031   moves_[0] = _startX | (_startY<<8);
0032   moveIndex_ = 1;
0033   finished_ = false;
0034 
0035 #ifndef NDEBUG
0036   lastX_ = _startX;
0037   lastY_ = _startY;
0038 #endif
0039 }
0040 
0041 Move::~Move () {
0042   delete [] moves_;
0043 }
0044 
0045 void
0046 Move::finish () {
0047   assert (!finished_);
0048   assert (moveIndex_ > 1);
0049 
0050   unsigned short *newMoves = new unsigned short[moveIndex_];
0051   memcpy (newMoves, moves_, moveIndex_*sizeof (unsigned short));
0052   delete [] moves_;
0053   moves_ = newMoves;
0054 
0055   finished_ = true;
0056 }
0057 
0058 void
0059 Move::save (QString &s) {
0060   static const char move1[] = "lrud";
0061   static const char push1[] = "LRUD";
0062   static const char move2[] = "wens";
0063   static const char push2[] = "WENS";
0064 
0065   assert (finished_);
0066   int x=startX ();
0067   int y=startY ();
0068   int pos=1;
0069 
0070   int x2, y2, dist=0;
0071   int dir=-1;
0072   bool push=false;
0073   while (pos<moveIndex_) {
0074     if (dir >= 0) s += push ? push1[dir] : move1[dir];
0075 
0076     x2 = moves_[pos]&0x7f;
0077     y2 = (moves_[pos]>>8)&0x7f;
0078     push = (moves_[pos++]&0x80)==0x80;
0079 
0080     if (x2<x) {
0081       dir = 0;
0082       dist = x-x2;
0083     } else if (x2>x) {
0084       dir = 1;
0085       dist = x2-x;
0086     } else if (y2<y) {
0087       dir = 2;
0088       dist = y-y2;
0089     } else if (y2>y) {
0090       dir = 3;
0091       dist = y2-y;
0092     } else {
0093       assert (0);
0094     }
0095     assert (dist > 0);
0096 
0097     if (dist > 1) {
0098       if (dist>=10) {
0099     s += '0' + (dist/10);
0100     dist %= 10;
0101       }
0102       s += '0' + dist;
0103     }
0104 
0105     x = x2;
0106     y = y2;
0107   }
0108 
0109   if (dir >= 0) s += push ? push2[dir] : move2[dir];
0110 }
0111 
0112 const char *
0113 Move::load (const char *s) {
0114   assert (!finished_);
0115   int x=finalX ();
0116   int y=finalY ();
0117 
0118   int dist;
0119   bool last=false;
0120   char c;
0121   while ((c = *s++) != '\0') {
0122     dist = 1;
0123     if (c >= '0' && c <= '9') {
0124       dist = c - '0';
0125       c = *s++;
0126       if (c >= '0' && c <= '9') {
0127     dist = 10*dist + c - '0';
0128     c = *s++;
0129       }
0130     }
0131 
0132     switch (tolower (c)) {
0133     case 'w':
0134       last = true;
0135       /* Fall through */
0136     case 'l':
0137       x -= dist;
0138       break;
0139     case 'e':
0140       last = true;
0141       /* Fall through */
0142     case 'r':
0143       x += dist;
0144       break;
0145     case 'n':
0146       last = true;
0147       /* Fall through */
0148     case 'u':
0149       y -= dist;
0150       break;
0151     case 's':
0152       last = true;
0153       /* Fall through */
0154     case 'd':
0155       y += dist;
0156       break;
0157 
0158     default:
0159       //printf ("2><>%s\n", s);
0160       //abort ();
0161       return nullptr;
0162     }
0163 
0164     if (x<=0 || x>=MAX_X || y<=0 || y>=MAX_Y) {
0165       //printf ("x: %d, y:%d ><>%s\n", x, y, s);
0166       //abort ();
0167 
0168       return nullptr;
0169     }
0170 
0171     if (isupper (c)) push (x, y);
0172     else step (x, y);
0173 
0174     if (last) break;
0175   }
0176   finish ();
0177 
0178   return s;
0179 }
0180 
0181 bool
0182 Move::redo (LevelMap *map) {
0183   assert (finished_);
0184 
0185   for (int pos=1; pos<moveIndex_; pos++) {
0186     int x = moves_[pos]&0x7f;
0187     int y = (moves_[pos]>>8)&0x7f;
0188     bool push = (moves_[pos]&0x80)==0x80;
0189     bool ret;
0190 
0191     if (push) ret = map->push (x, y);
0192     else ret = map->step (x, y);
0193 
0194     if (!ret) return false;
0195   }
0196 
0197   return true;
0198 }
0199 
0200 bool
0201 Move::undo (LevelMap *map) {
0202   assert (finished_);
0203 
0204   for (int pos=moveIndex_-2; pos>=0; --pos) {
0205     int x = moves_[pos]&0x7f;
0206     int y = (moves_[pos]>>8)&0x7f;
0207     bool push = (moves_[pos+1]&0x80)==0x80;
0208     bool ret;
0209 
0210     if (push) ret = map->unpush (x, y);
0211     else ret = map->unstep (x, y);
0212 
0213     if (!ret) return false;
0214   }
0215 
0216   return true;
0217 }