Changeset 5


Ignore:
Timestamp:
Dec 17, 2005, 7:12:54 AM (14 years ago)
Author:
jtv
Message:

Implemented saving/restoring of game state

Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Makefile

    r2 r5  
    11#! /usr//bin/make
    22
    3 OBJS=ui_cli.o gamelogic.o c_abi.o
     3OBJS=ui_cli.o gamelogic.o c_abi.o save.o
    44DELIVERABLES=ui_cli libmines.a
    55CXXFLAGS=-O2 -g \
     
    2121    $(RM) $(OBJS)
    2222
    23 ui_cli: ui_cli.o gamelogic.o
     23distclean: clean
     24    $(RM) $(DELIVERABLES)
     25
     26ui_cli: ui_cli.o libmines.a
    2427    $(CXX) $(LDFLAGS) $(LOADLIBES) $^ -o $@
    2528
    2629library: libmines.a
    2730
    28 libmines.a: c_abi.o gamelogic.o
     31libmines.a: c_abi.o gamelogic.o save.o
     32    $(AR) rc $@ $^
    2933
    3034ui_cli.o: ui_cli.cxx gamelogic.hxx
    3135    $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
    3236
    33 gamelogic.o: gamelogic.cxx gamelogic.hxx
     37gamelogic.o: gamelogic.cxx gamelogic.hxx save.hxx
    3438    $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
    3539
     
    3741    $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
    3842
     43save.o: save.cxx save.hxx
     44    $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
     45
    3946.PHONY: all library
  • trunk/c_abi.cxx

    r4 r5  
    4545
    4646
     47Minefield *mines_load(const char buffer[])
     48{
     49  return new Lake(buffer);
     50}
     51
     52
    4753void mines_close(Minefield *f)
    4854{
    4955  delete castback(f);
     56}
     57
     58
     59int mines_save(Minefield *f, char buffer[])
     60{
     61  return castback(f)->save(buffer);
    5062}
    5163
     
    97109}
    98110
     111int mines_rows(const Minefield *f)
     112{
     113  return castback(f)->rows();
     114}
     115
     116int mines_cols(const Minefield *f)
     117{
     118  return castback(f)->cols();
     119}
     120
    99121} // extern "C"
    100122
  • trunk/c_abi.h

    r4 r5  
    3131Minefield *mines_init(int rows, int cols, int mines);
    3232
     33/** @brief Reload game state from memory buffer filled by mines_save()
     34 */
     35Minefield *mines_load(const char buffer[]);
     36
     37/** @brief Save minefield to buffer (at least rows*cols+100 bytes large).
     38 * @return Number of bytes of buffer space used (not including terminating zero)
     39 */
     40int mines_save(Minefield *, char buffer[]);
     41
    3342/** @brief Maximum intelligence level implemented by current version
    3443 */
     
    6271int mines_togo(const Minefield *);
    6372
     73/** @brief Number of rows in minefield
     74 */
     75int mines_rows(const Minefield *);
     76
     77/** @brief Number of columns in minefield
     78 */
     79int mines_cols(const Minefield *);
     80
    6481#ifdef __cplusplus
    6582}
  • trunk/gamelogic.cxx

    r4 r5  
    2121
    2222#include <cassert>
    23 #include <set>
     23#include <stdexcept>
     24#include <string>
    2425#include <vector>
    2526
    2627#include "gamelogic.hxx"
     28#include "save.hxx"
    2729
    2830using namespace std;
     
    3638  return rand() % top;
    3739}
    38 }
     40} // namespace
    3941
    4042
     
    205207  }
    206208};
     209
     210
    207211} // namespace
    208212
    209213
    210 Lake::Lake(int rows, int cols, int mines) :
     214Lake::Lake(int _rows, int _cols, int mines) :
    211215  m_patches(0),
    212   m_rows(rows),
    213   m_cols(cols),
     216  m_rows(_rows),
     217  m_cols(_cols),
    214218  m_intelligence(1),
    215   m_patches_to_go(rows*cols-mines),
     219  m_patches_to_go(m_rows*m_cols-mines),
    216220  m_moves(0)
    217221{
    218   assert(rows > 0);
    219   assert(cols > 0);
     222  assert(m_rows > 0);
     223  assert(m_cols > 0);
    220224  assert(mines > 0);
    221   assert(mines <= rows*cols);
     225  assert(mines <= m_rows*m_cols);
    222226
    223227  m_patches = new Patch[arraysize()];
     
    226230  {
    227231    const int row = rand_coord(m_rows), col = rand_coord(m_cols);
    228     if (!at(row,col).mined())
    229     {
    230       at(row,col).mine();
    231       for_neighbours(row,col,set_nearby_mine());
    232       --mines;
    233     }
     232    mines -= place_mine_at(row,col);
    234233  }
    235234
     
    244243
    245244
     245Lake::Lake(const char buffer[]) :
     246  m_patches(0),
     247  m_rows(0),
     248  m_cols(0),
     249  m_intelligence(0),
     250  m_patches_to_go(0),
     251  m_moves(0)
     252{
     253  initialize_encoding();
     254
     255  const char *here = read_header(buffer);
     256
     257  m_rows = read_int("rows",here);
     258  m_cols = read_int("cols",here);
     259  m_moves = read_int("move",here);
     260  m_intelligence = read_int("intl",here);
     261  m_patches_to_go = m_rows * m_cols;
     262
     263  // TODO: Re-unify this with regular constructor
     264  assert(m_rows > 0);
     265  assert(m_cols > 0);
     266  assert(m_moves >= 0);
     267  assert(m_intelligence >= 0);
     268
     269  m_patches = new Patch[arraysize()];
     270
     271  here = skip_whitespace(here);
     272
     273  // TODO: Unify these two read operations
     274
     275  const int padding = linepadding(m_cols);
     276
     277  // Read mine placement
     278  for (int r=0; r<m_rows; ++r)
     279  {
     280    for (int c = 0; c < m_cols; c += bitsperchar)
     281    {
     282      unsigned int x = extract_char(here);
     283      for (int i = 0; i < bitsperchar; ++i)
     284      {
     285    if (x & 1) m_patches_to_go -= place_mine_at(r,c+i);
     286    x >>= 1;
     287      }
     288    }
     289    here = read_eol(here, padding);
     290  }
     291
     292  // Read revealed fields
     293  for (int r=0; r<m_rows; ++r)
     294  {
     295    for (int c = 0; c < m_cols; c += bitsperchar)
     296    {
     297      unsigned int x = extract_char(here);
     298      for (int i = 0; i < bitsperchar; ++i)
     299      {
     300    if (x & 1)
     301    {
     302      reveal_patch(r, c+i);
     303      --m_patches_to_go;
     304    }
     305    x >>= 1;
     306      }
     307    }
     308    here = read_eol(here, padding);
     309  }
     310
     311  // TODO: Re-unify this with regular constructor
     312  for (int b=1; b<border; ++b)
     313  {
     314    border_row(-b);
     315    border_row(m_rows+b-1);
     316    border_col(-b);
     317    border_col(m_cols+b-1);
     318  }
     319}
     320
     321
    246322Lake::~Lake() throw ()
    247323{
     
    249325}
    250326
     327
     328int Lake::save(char buf[]) const
     329{
     330  initialize_encoding();
     331  char *here = write_header(buf);
     332  here = write_int("rows",here,m_rows);
     333  here = write_int("cols",here,m_cols);
     334  here = write_int("move",here,m_moves);
     335  here = write_int("intl",here,m_intelligence);
     336  here = write_newline(here);
     337
     338  // Padding at end of line required by base64
     339  const int padding = linepadding(m_cols);
     340
     341  // TODO: Unify these two write actions
     342
     343  // Write mines
     344  for (int r = 0; r < m_rows; ++r)
     345  {
     346    for (int c = 0; c < m_cols; c += bitsperchar)
     347    {
     348      unsigned int x = 0;
     349      for (int i = bitsperchar-1; i >= 0; --i)
     350      {
     351    x <<= 1;
     352    if (at(r,c+i).mined()) x |= 1;
     353      }
     354      *here++ = produce_char(x);
     355    }
     356    here = write_eol(here, padding);
     357  }
     358
     359  here = write_newline(here);
     360
     361  // Write revealed fields
     362  for (int r = 0; r < m_rows; ++r)
     363  {
     364    for (int c = 0; c < m_cols; c += bitsperchar)
     365    {
     366      unsigned int x = 0;
     367      for (int i = bitsperchar-1; i >= 0; --i)
     368      {
     369    x <<= 1;
     370    if (at(r,c+i).revealed()) x |= 1;
     371      }
     372      *here++ = produce_char(x);
     373    }
     374    here = write_eol(here, padding);
     375  }
     376  terminate(here);
     377
     378  return here - buf;
     379}
     380
     381
     382bool Lake::place_mine_at(int row, int col)
     383{
     384  Patch &p = at(row,col);
     385  if (p.mined()) return false;
     386
     387  p.mine();
     388  for_neighbours(row,col,set_nearby_mine());
     389  return true;
     390}
    251391
    252392const Patch &Lake::at(int row, int col) const
     
    290430}
    291431
    292 void Lake::reveal_patch(int row, int col, set<Coords> &changes)
     432void Lake::reveal_patch(int row, int col)
    293433{
    294434  Patch &p = at(row,col);
     
    296436  {
    297437    p.reveal();
    298     changes.insert(Coords(row,col));
    299438    for_neighbours(row,col,reveal_nearby(p.mined()));
    300439  }
     
    303442void Lake::border_row(int r)
    304443{
    305   set<Coords> changes;
    306   for (int c=m_cols+border-1; c>=-border; --c) reveal_patch(r,c,changes);
     444  for (int c=m_cols+border-1; c>=-border; --c) reveal_patch(r,c);
    307445}
    308446
    309447void Lake::border_col(int c)
    310448{
    311   set<Coords> changes;
    312   for (int r=m_rows-1; r>=0; --r) reveal_patch(r,c,changes);
     449  for (int r=m_rows-1; r>=0; --r) reveal_patch(r,c);
    313450}
    314451
     
    330467        if (!p.revealed())
    331468        {
    332           reveal_patch(row,col,changes);
     469          reveal_patch(row,col);
     470      changes.insert(Coords(row,col));
    333471          if (!p.mined()) --m_patches_to_go;
    334472          for_zone<2,true>(row,col,set_add<UnfinishedPatch>(area));
  • trunk/gamelogic.hxx

    r4 r5  
    5656{
    5757public:
    58   explicit Lake(int rows, int cols, int mines);
     58  /// Start new game
     59  Lake(int rows, int cols, int mines);
     60  /// Start game from saved game state
     61  explicit Lake(const char[]);
     62
    5963  ~Lake() throw ();
    6064
     
    9094  char status_at(int row, int col) const;
    9195
     96  const int rows() const throw () { return m_rows; }
     97  const int cols() const throw () { return m_cols; }
     98
     99  /// Write game state (in ASCII) to memory buffer
     100  /** The available buffer space must be at least rows*cols+100 bytes large.
     101   */
     102  int save(char buf[]) const;
     103
    92104private:
    93105  enum { border = 3 };
    94106  Patch &at(int row, int col);
    95107  const Patch &at(int row, int col) const;
     108
     109  void init_field();
     110
     111  bool place_mine_at(int row, int col);
    96112
    97113  /// Apply functor f to a square of Patches centered at (row,col)
     
    115131    { return for_zone<1,false>(row,col,f); }
    116132
    117   void reveal_patch(int row, int col, std::set<Coords> &changes);
     133  void reveal_patch(int row, int col);
    118134
    119135  /// Initialize a row of patches in the lake's border
  • trunk/ui_cli.cxx

    r4 r5  
    2020// A very basic sample user interface for a Minesweeper game based on libmines
    2121
     22#include <cerrno>
    2223#include <iostream>
     24#include <stdexcept>
     25
     26#include <fcntl.h>
     27#include <unistd.h>
    2328
    2429#include "gamelogic.hxx"
     
    4045}
    4146
     47void save_game(const Lake &L)
     48{
     49  char *const buf = new char[L.rows()*L.cols()+100];
     50  int fd = -1;
     51  try
     52  {
     53    int bytes = L.save(buf);
     54    fd = creat("mines.savedgame", 0744);
     55    if (fd == -1) throw runtime_error(strerror(errno));
     56    write(fd, buf, bytes);
     57
     58#ifndef NDEBUG
     59    // See if we can restore this game, save that, and get the same data back
     60    const Lake L2(buf);
     61    char *const check = new char[L.rows()*L.cols()+100];
     62    bytes = L2.save(check);
     63    assert(strcmp(buf,check) == 0);
     64    delete [] check;
     65    cout << "(Game saved and checked)" << endl;
     66#endif
     67  }
     68  catch (const exception &e)
     69  {
     70    cerr << e.what() << endl;
     71  }
     72  delete [] buf;
     73  if (fd != -1) close(fd);
     74}
     75
     76
     77
    4278} // namespace
    4379
     
    5591    while (L.to_go())
    5692    {
     93      save_game(L);
    5794      cout << "Clear patches to go: " << L.to_go() << "..." << endl;
    5895      coordsbar(cols);
     
    83120    "Please enter row and column (zero-based, separated by space) of next "
    84121    "patch you think is mine-free, or make either coordinate negative if "
    85     "you want to indicate a mine: "
     122    "you want to indicate a mine:"
    86123    << endl;
    87124      int r, c;
     
    92129      if (r < 0 || c < 0)
    93130      {
    94     thinksismine = true;
    95     r = abs(r);
    96     c = abs(c);
     131        thinksismine = true;
     132        r = abs(r);
     133        c = abs(c);
    97134      }
    98135      if (r >= rows || c >= cols) cout<<"Out of range!"<<endl;
Note: See TracChangeset for help on using the changeset viewer.