void Board::setSize(int x, int y) { if(x == x_tiles() && y == y_tiles()) return; if(field != 0) delete [] field; field = new int[ x * y ]; _x_tiles = x; _y_tiles = y; for(int i = 0; i < x; i++) for(int j = 0; j < y; j++) setField(i, j, EMPTY); // set the minimum size of the scalable window const double MINIMUM_SCALE = 0.2; int w = qRound(tiles.unscaledTileWidth() * MINIMUM_SCALE) * x_tiles(); int h = qRound(tiles.unscaledTileHeight() * MINIMUM_SCALE) * y_tiles(); w += tiles.unscaledTileWidth(); h += tiles.unscaledTileWidth(); setMinimumSize(w, h); resizeBoard(); newGame(); emit changed(); }
void Board::setField(int x, int y, int value) { if(x < x_tiles() && y < y_tiles()) field[y * x_tiles() + x] = value; else { fprintf(stderr, "write access to invalid field (%d,%d)\n", x, y); exit(1); } }
void Board::setField(int x, int y, int value) { if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) { kdFatal() << "Attempted write to invalid field position " "(" << x << ", " << y << ")" << endl; } field[y * x_tiles() + x] = value; }
int Board::tilesLeft() { int left = 0; for(int i = 0; i < x_tiles(); i++) for(int j = 0; j < x_tiles(); j++) if(getField(i, j) != EMPTY) left++; return left; }
int Board::getField(int x, int y) { if(x == x_tiles() || x == -1) return EMPTY; else if(y == y_tiles() || y == -1) return EMPTY; else if(x < -1 || x > x_tiles() || y < -1 || y > y_tiles()) { return EMPTY; fprintf(stderr, "read access to invalid field (%d,%d)\n", x, y); exit(1); return 0; // makes gcc happy } else return field[y * x_tiles() + x]; }
int Board::getField(int x, int y) const { #ifdef DEBUGGING if(x < -1 || y < -1 || x > x_tiles() || y > y_tiles()) { kdFatal() << "Attempted read from invalid field position " "(" << x << ", " << y << ")" << endl; } #endif if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) return EMPTY; return field[y * x_tiles() + x]; }
bool Board::getHint_I(int &x1, int &y1, int &x2, int &y2, History h[4]) { short done[45]; for( short index = 0; index < 45; index++ ) done[index] = 0; // remember old history History old[4]; for(int i = 0; i < 4; i++) old[i] = history[i]; // initial no hint x1 = -1; x2 = -1; y1 = -1; y2 = -1; for(int x = 0; x < x_tiles(); x++) for(int y = 0; y < y_tiles(); y++) if(getField(x, y) != EMPTY && done[getField(x, y)] != 4) { int tile = getField(x, y); // for all these types of tile search path's for(int xx = 0; xx < x_tiles(); xx++) for(int yy = 0; yy < y_tiles(); yy++) if(xx != x || yy != y) if(getField(xx, yy) == tile) if(findPath(x, y, xx, yy)) { for(int i = 0; i < 4; i++) h[i] = history[i]; x1 = x; x2 = xx; y1 = y; y2 = yy; for(int i = 0; i < 4; i++) history[i] = old[i]; return TRUE; } clearHistory(); done[tile]++; } for(int i = 0; i < 4; i++) history[i] = old[i]; return FALSE; }
void Board::setSize(int x, int y) { if(x == x_tiles() && y == y_tiles()) return; if(field != 0) free(field); field = (int*)malloc(sizeof(int) * x * y); _x_tiles = x; _y_tiles = y; for(int i = 0; i < x; i++) for(int j = 0; j < y; j++) setField(i, j, EMPTY); loadTiles(); double scaler = 1.0; while(sizeHint().width() > kapp->desktop()->width() - 2*XBORDER || sizeHint().height() > kapp->desktop()->height() - 2*YBORDER) { scaler -= 0.2; loadTiles(scaler); } newGame(); emit changed(); emit sizeChange(); }
bool Board::findPath(int x1, int y1, int x2, int y2, Path& p) const { p.clear(); if(findSimplePath(x1, y1, x2, y2, p)) return true; // Find a path of 3 segments const int dx[4] = { 1, 0, -1, 0 }; const int dy[4] = { 0, 1, 0, -1 }; for(int i = 0; i < 4; i++) { int newx = x1 + dx[i]; int newy = y1 + dy[i]; while(newx >= -1 && newx <= x_tiles() && newy >= -1 && newy <= y_tiles() && getField(newx, newy) == EMPTY) { if(findSimplePath(newx, newy, x2, y2, p)) { p.push_front(Position(x1, y1)); return true; } newx += dx[i]; newy += dy[i]; } } return false; }
void Board::mousePressEvent(QMouseEvent *e) { // calculate position int pos_x = (e->pos().x() - XBORDER <0)?-1: (e->pos().x() - XBORDER) / pm_tile[0]->width(); int pos_y = (e->pos().y() - YBORDER <0)?-1: (e->pos().y() - YBORDER) / pm_tile[0]->height(); // Mark tile if(e->button() == LeftButton) { if(highlighted_tile != -1) { int oldmarkx = mark_x; int oldmarky = mark_y; mark_x=-1; mark_y=-1; for(int i = 0; i < x_tiles(); i++) for(int j = 0; j < y_tiles(); j++){ if( highlighted_tile == getField(i, j)) updateField(i, j); } mark_x = oldmarkx; mark_y = oldmarky; // no tile selected highlighted_tile = -1; } if(pos_x >= 0 && pos_x < x_tiles() && pos_y >= 0 && pos_y < y_tiles()) emit fieldClicked(pos_x, pos_y); } // Assist by lighting all tiles of same type if(e->button() == RightButton) { int field = getField(pos_x,pos_y); highlighted_tile = field; for(int i = 0; i < x_tiles(); i++) for(int j = 0; j < y_tiles(); j++){ if( field == getField(i, j)){ mark_x=i; mark_y=j; } else{ mark_x=-1; mark_y=-1; } updateField(i, j); } mark_x=-1; mark_y=-1; // no tile selected } }
bool Board::getHint_I(Path& p) const { //dumpBoard(); short done[TileSet::nTiles]; for( short index = 0; index < TileSet::nTiles; index++ ) done[index] = 0; for(int x = 0; x < x_tiles(); x++) { for(int y = 0; y < y_tiles(); y++) { int tile = getField(x, y); if(tile != EMPTY && done[tile - 1] != 4) { // for all these types of tile search path's for(int xx = 0; xx < x_tiles(); xx++) { for(int yy = 0; yy < y_tiles(); yy++) { if(xx != x || yy != y) { if(getField(xx, yy) == tile) if(findPath(x, y, xx, yy, p)) { //kdDebug() << "path.size() == " << p.size() << endl; //for(Path::const_iterator i = p.begin(); i != p.end(); ++i) // kdDebug() << "pathEntry: (" << i->x << ", " << i->y // << ") => " << getField(i->x, i->y) << endl; return true; } } } } done[tile - 1]++; } } } return false; }
void Board::clearHighlight() { if(highlighted_tile != -1) { int old_highlight = highlighted_tile; highlighted_tile = -1; for(int i = 0; i < x_tiles(); i++) for(int j = 0; j < y_tiles(); j++) if(old_highlight == getField(i, j)) updateField(i, j, false); } }
QPoint Board::midCoord(int x, int y) const { QPoint p; int w = tiles.tileWidth(); int h = tiles.tileHeight(); if(x == -1) p.setX(xOffset() - (w / 4)); else if(x == x_tiles()) p.setX(xOffset() + (w * x_tiles()) + (w / 4)); else p.setX(xOffset() + (w * x) + (w / 2)); if(y == -1) p.setY(yOffset() - (w / 4)); else if(y == y_tiles()) p.setY(yOffset() + (h * y_tiles()) + (w / 4)); else p.setY(yOffset() + (h * y) + (h / 2)); return p; }
QPoint Board::midCoord(int x, int y) { QPoint p; int w = pm_tile[0]->width(); int h = pm_tile[0]->height(); if(x == -1) p.setX(XBORDER/2 - w/2); else if(x == x_tiles()) p.setX(XBORDER/2 + w * x_tiles()); else p.setX(XBORDER + w * x); if(y == -1) p.setY(YBORDER/2 - h/2); else if(y == y_tiles()) p.setY(YBORDER/2 + h * y_tiles()); else p.setY(YBORDER + h * y); p.setX(p.x() + w/2); p.setY(p.y() + h/2); return p; }
bool Board::solvable(bool norestore) { int x1, y1, x2, y2; History h[4]; int *oldfield = 0; if(!norestore) { oldfield = (int *)malloc(x_tiles() * y_tiles() * sizeof(int)); memcpy(oldfield, field, x_tiles() * y_tiles() * sizeof(int)); } while(getHint_I(x1, y1, x2, y2, h)) { setField(x1, y1, EMPTY); setField(x2, y2, EMPTY); } int left = tilesLeft(); if(!norestore) { memcpy(field, oldfield, x_tiles() * y_tiles() * sizeof(int)); free(oldfield); } return (bool)(left == 0); }
bool Board::solvable(bool norestore) { int *oldfield = 0; if(!norestore) { oldfield = new int [x_tiles() * y_tiles()]; memcpy(oldfield, field, x_tiles() * y_tiles() * sizeof(int)); } Path p; while(getHint_I(p)) { kdFatal(getField(p.front().x, p.front().y) != getField(p.back().x, p.back().y)) << "Removing unmateched tiles: (" << p.front().x << ", " << p.front().y << ") => " << getField(p.front().x, p.front().y) << " (" << p.back().x << ", " << p.back().y << ") => " << getField(p.back().x, p.back().y) << endl; setField(p.front().x, p.front().y, EMPTY); setField(p.back().x, p.back().y, EMPTY); //if(gravityFlag()) //{ // gravity(p.front().x, false); // gravity(p.back().x, false); //} } int left = tilesLeft(); if(!norestore) { memcpy(field, oldfield, x_tiles() * y_tiles() * sizeof(int)); delete [] oldfield; } return (bool)(left == 0); }
void Board::dumpBoard() const { kdDebug() << "Board contents:" << endl; for(int y = 0; y < y_tiles(); ++y) { QString row; for(int x = 0; x < x_tiles(); ++x) { int tile = getField(x, y); if(tile == EMPTY) row += " --"; else row += QString("%1").arg(getField(x, y), 3); } kdDebug() << row << endl; } }
void Board::paintEvent(QPaintEvent *e) { QRect ur = e->rect(); // rectangle to update QPixmap pm(ur.size()); // Pixmap for double-buffering pm.fill(this, ur.topLeft()); // fill with widget background QPainter p(&pm); p.translate(-ur.x(), -ur.y()); // use widget coordinate system if(paused) { p.setFont(KGlobalSettings::largeFont()); p.drawText(rect(), Qt::AlignCenter, i18n("Game Paused")); } else { int w = tiles.tileWidth(); int h = tiles.tileHeight(); for(int i = 0; i < x_tiles(); i++) { for(int j = 0; j < y_tiles(); j++) { int tile = getField(i, j); if(tile == EMPTY) continue; int xpos = xOffset() + i * w; int ypos = yOffset() + j * h; QRect r(xpos, ypos, w, h); if(e->rect().intersects(r)) { if(isTileHighlighted(i, j)) p.drawPixmap(xpos, ypos, tiles.highlightedTile(tile-1)); else p.drawPixmap(xpos, ypos, tiles.tile(tile-1)); } } } } p.end(); bitBlt( this, ur.topLeft(), &pm ); }
bool Board::findPath(int x1, int y1, int x2, int y2) { clearHistory(); if(findSimplePath(x1, y1, x2, y2)) return TRUE; else { // find 3-way path int dx[4] = {1, 0, -1, 0}; int dy[4] = {0, 1, 0, -1}; int i; for(i = 0; i < 4; i++) { int newx = x1 + dx[i], newy = y1 + dy[i]; while(getField(newx, newy) == EMPTY && newx >= -1 && newx <= x_tiles() && newy >= -1 && newy <= y_tiles()) { if(findSimplePath(newx, newy, x2, y2)) { // make place for history point for(int j = 3; j > 0; j--) history[j] = history[j-1]; // insert history point history[0].x = x1; history[0].y = y1; return TRUE; } newx += dx[i]; newy += dy[i]; } } clearHistory(); return FALSE; } return FALSE; }
void Board::paintEvent(QPaintEvent *e) { QPainter p; p.begin(this); for(int i = 0; i < x_tiles(); i++) for(int j = 0; j < y_tiles(); j++) { if(getField(i, j) == EMPTY) continue; int xpos = XBORDER + i * pm_tile[1]->width(); int ypos = YBORDER + j * pm_tile[1]->height(); QRect r(xpos, ypos, pm_tile[1]->width(), pm_tile[1]->height()); if(e->rect().intersects(r)) { // check if it is a marked piece if(i == mark_x && j == mark_y) { QPixmap *lpm = lighten(pm_tile[getField(i, j)-1]); p.drawPixmap(xpos, ypos, *lpm); delete lpm; } else p.drawPixmap(xpos, ypos, *pm_tile[getField(i, j)-1]); } } p.end(); }
QSize Board::sizeHint() { return QSize(x_tiles() * pm_tile[0]->width() + 2 * XBORDER, y_tiles() * pm_tile[0]->height() + 2 * YBORDER); }
int tile_w(void) const noexcept { return (width()/x_tiles())+((tile_i()+1 == x_tiles())?1:0); }
int tile_x(void) const noexcept { return tile_i()*(width()/x_tiles()); }
bool multiple_tiles(void) const noexcept { return x_tiles() > 1 || y_tiles() > 1; }
void Board::newGame() { //kdDebug() << "NewGame" << endl; int i, x, y, k; mark_x = -1; mark_y = -1; highlighted_tile = -1; // will clear previous highlight _undo.clear(); _redo.clear(); connection.clear(); // distribute all tiles on board int cur_tile = 1; for(y = 0; y < y_tiles(); y += 4) { for(x = 0; x < x_tiles(); ++x) { for(k = 0; k < 4 && y + k < y_tiles(); k++) setField(x, y + k, cur_tile); cur_tile++; if(cur_tile > TileSet::nTiles) cur_tile = 1; } } if(getShuffle() == 0) { update(); starttime = time((time_t *)0); emit changed(); return; } // shuffle the field int tx = x_tiles(); int ty = y_tiles(); for(i = 0; i < x_tiles() * y_tiles() * getShuffle(); i++) { int x1 = random.getLong(tx); int y1 = random.getLong(ty); int x2 = random.getLong(tx); int y2 = random.getLong(ty); int t = getField(x1, y1); setField(x1, y1, getField(x2, y2)); setField(x2, y2, t); } // do not make solvable if _solvable_flag is false if(!_solvable_flag) { update(); starttime = time((time_t *)0); emit changed(); return; } int fsize = x_tiles() * y_tiles() * sizeof(int); int *oldfield = new int[x_tiles() * y_tiles()]; memcpy(oldfield, field, fsize); // save field int *tiles = new int[x_tiles() * y_tiles()]; int *pos = new int[x_tiles() * y_tiles()]; while(!solvable(true)) { //kdDebug() << "Not solvable" << endl; //dumpBoard(); // generate a list of free tiles and positions int num_tiles = 0; for(i = 0; i < x_tiles() * y_tiles(); i++) if(field[i] != EMPTY) { pos[num_tiles] = i; tiles[num_tiles] = field[i]; num_tiles++; } // restore field memcpy(field, oldfield, fsize); // redistribute unsolved tiles while(num_tiles > 0) { // get a random tile int r1 = random.getLong(num_tiles); int r2 = random.getLong(num_tiles); int tile = tiles[r1]; int apos = pos[r2]; // truncate list tiles[r1] = tiles[num_tiles-1]; pos[r2] = pos[num_tiles-1]; num_tiles--; // put this tile on the new position field[apos] = tile; } // remember field memcpy(oldfield, field, fsize); } // restore field memcpy(field, oldfield, fsize); delete tiles; delete pos; delete oldfield; update(); starttime = time((time_t *)0); emit changed(); }
QSize Board::unscaledSize() const { int w = tiles.unscaledTileWidth() * x_tiles() + tiles.unscaledTileWidth(); int h = tiles.unscaledTileHeight() * y_tiles() + tiles.unscaledTileWidth(); return QSize(w, h); }
void Board::resizeBoard() { // calculate tile size required to fit all tiles in the window int w = static_cast<int>( static_cast<double>(width() - tiles.unscaledTileWidth()) / x_tiles() ); int h = static_cast<int>( static_cast<double>(height() - tiles.unscaledTileWidth()) / y_tiles() ); const double MAXIMUM_SCALE = 2.0; w = std::min(w, static_cast<int>((tiles.unscaledTileWidth() * MAXIMUM_SCALE) + 0.5)); h = std::min(h, static_cast<int>((tiles.unscaledTileHeight() * MAXIMUM_SCALE) + 0.5)); tiles.resizeTiles(w, h); }
// The board is centred inside the main playing area. xOffset/yOffset provide // the coordinates of the top-left corner of the board. int Board::xOffset() const { return (width() - (tiles.tileWidth() * x_tiles())) / 2; }
void Board::mousePressEvent(QMouseEvent *e) { // Calculate field position int pos_x = (e->pos().x() - xOffset()) / tiles.tileWidth(); int pos_y = (e->pos().y() - yOffset()) / tiles.tileHeight(); if(e->pos().x() < xOffset() || e->pos().y() < yOffset() || pos_x >= x_tiles() || pos_y >= y_tiles()) { pos_x = -1; pos_y = -1; } // Mark tile if(e->button() == LeftButton) { clearHighlight(); if(pos_x != -1) marked(pos_x, pos_y); } // Assist by highlighting all tiles of same type if(e->button() == RightButton) { int clicked_tile = getField(pos_x, pos_y); // Clear marked tile if(mark_x != -1 && getField(mark_x, mark_y) != clicked_tile) { // We need to set mark_x and mark_y to -1 before calling // updateField() to ensure the tile is redrawn as unmarked. int oldmarkx = mark_x; int oldmarky = mark_y; mark_x = -1; mark_y = -1; updateField(oldmarkx, oldmarky, false); } else { mark_x = -1; mark_y = -1; } // Perform highlighting if(clicked_tile != highlighted_tile) { int old_highlighted = highlighted_tile; highlighted_tile = clicked_tile; for(int i = 0; i < x_tiles(); i++) { for(int j = 0; j < y_tiles(); j++) { const int field_tile = getField(i, j); if(field_tile != EMPTY) { if(field_tile == old_highlighted) updateField(i, j, false); else if(field_tile == clicked_tile) updateField(i, j, false); } } } } } }
void Board::newGame() { int i, j, x, y, k; mark_x = -1; mark_y = -1; while(_undo.count()) _undo.removeFirst(); while(_redo.count()) _redo.removeFirst(); clearHistory(); for(i = 0; i < x_tiles(); i++) for(j = 0; j < y_tiles(); j++) setField(i, j, EMPTY); // distribute all tiles on board int cur_tile = 0; for(i = 0; i < x_tiles() * y_tiles() * 12; i++) { // map the tileindex to a tile // not all tiles from the pixmap are really used, only // 36 out of 45 are used. This maps and index to // the "real" index. int tile; if(cur_tile == 28) tile = 30; else if(cur_tile >= 29 && cur_tile <= 35) tile = cur_tile + 7; else tile = cur_tile; cur_tile++; if(cur_tile == 36) cur_tile = 0; x = i % x_tiles(); y = i / x_tiles() * 4; tile++; for(k = 0; k < 4 && k + y < y_tiles(); k++) setField(x, y+k, tile); } if(getShuffle() == 0) { if(!trying) { update(); starttime = time((time_t *)0); emit changed(); } return; } // shuffle the field int tx = x_tiles(); int ty = y_tiles(); for(i = 0; i < x_tiles() * y_tiles() * getShuffle(); i++) { int x1 = random(tx); int y1 = random(ty); int x2 = random(tx); int y2 = random(ty); int t = getField(x1, y1); setField(x1, y1, getField(x2, y2)); setField(x2, y2, t); } // do not make solvable if _solvable_flag is FALSE if(!_solvable_flag) { if(!trying) { update(); starttime = time((time_t *)0); emit changed(); } return; } int fsize = x_tiles() * y_tiles() * sizeof(int); int *oldfield = new int[x_tiles() * y_tiles()]; memcpy(oldfield, field, fsize); // save field int *tiles = new int[x_tiles() * y_tiles()]; int *pos = new int[x_tiles() * y_tiles()]; while(!solvable(TRUE)) { // generate a list of free tiles and positions int num_tiles = 0; for(i = 0; i < x_tiles() * y_tiles(); i++) if(field[i] != EMPTY) { pos[num_tiles] = i; tiles[num_tiles] = field[i]; num_tiles++; } // restore field memcpy(field, oldfield, fsize); // redistribute unsolved tiles while(num_tiles > 0) { // get a random tile int r1 = random(num_tiles); int r2 = random(num_tiles); int tile = tiles[r1]; int apos = pos[r2]; // truncate list tiles[r1] = tiles[num_tiles-1]; pos[r2] = pos[num_tiles-1]; num_tiles--; // put this tile on the new position field[apos] = tile; } // remember field memcpy(oldfield, field, fsize); } // restore field memcpy(field, oldfield, fsize); delete tiles; delete pos; delete oldfield; if(!trying) { update(); starttime = time((time_t *)0); emit changed(); } }