int localGame() {
    system("cls");
    char winner = 0;
    TILE board[121];
    memset(&board, 0, sizeof board);
    initBoard(board);
    char currentPlayer = 1;
    TILE t = {0,0,0,0}, g;
    do {
        jumpTo(0,0);
        printf("%s, make a move!\n", currentPlayer==1?"Attacker":"Defender");
        printBoard(board);
        int r;
        t = selectPiece(board, currentPlayer, t);
        if (tileEmpty(t)) return 0;
        printIntense(t);
        g = selectNewPosition(board, t, &r);
        if (tileEmpty(g)) return 0;
        if (r) continue;
        makeMove(t, g, board);
        winner = hasWinner(board, board[getIndex(g)]);
        currentPlayer = currentPlayer % 2 + 1;
        t = g;
    } while (!winner);
    
    // reblit board to capture last move
    jumpTo(0,1);
    printBoard(board);
    return winner;
}
//Flytta en pjäs
void Player:: makeMove(const bool &playerNr, Pieces *gamePlane[][8],int buttonX, int buttonY,int buttonXto, int buttonYto){
    int index = -1;//om vi hittade en pjäs så kommer ett idexet sätta in på denna varibel
    selectPiece(index,buttonX,buttonY);//väljer en pjäs och dess index
    if(index != -1){
        selectWhereTo(index, playerNr,gamePlane,buttonXto,buttonYto);//väljer en plats han vill flytta pjäsen vem är spelaren nr 1 eller 0
    }
    else{
        throw new exception();
        //"Det finns ingen pjäs där"
    }
}
Example #3
0
void undoMove()
{
  if(n_moves)
  {
    Piece *p1 = &moves[--n_moves];
    Piece *p2 = &moves[--n_moves];
    board[p1->x][p1->y][p1->z].value = p1->value;
    board[p2->x][p2->y][p2->z].value = p2->value;
    selectPiece(NULL);
    updatePiece(p1);
    updatePiece(p2);
    n_pairs_remaining++;
  }
}
Example #4
0
void
keyboard(unsigned char c, int x, int y)
{
  int piece;

  switch (c) {
  case 27:
    exit(0);
    break;
  case 'D':
  case 'd':
    if (solving) {
      freeSolutions();
      solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
      glutSetWindowTitle("glpuzzle");
      movingPiece = 0;
      changeState();
    }
    piece = selectPiece(x, y);
    if (piece) {
      nukePiece(piece);
    }
    glutPostRedisplay();
    break;
  case 'R':
  case 'r':
    reset();
    break;
  case 'S':
  case 's':
    toggleSolve();
    break;
  case 'b':
  case 'B':
    depth = 1 - depth;
    if (depth) {
      glEnable(GL_DEPTH_TEST);
    } else {
      glDisable(GL_DEPTH_TEST);
    }
    glutPostRedisplay();
    break;
  default:
    break;
  }
}
Example #5
0
void
mouse(int b, int s, int x, int y)
{
  float selx, sely;

  mousex = x;
  mousey = y;
  curX = x;
  curY = y;
  if (s == GLUT_DOWN) {
    switch (b) {
    case GLUT_LEFT_BUTTON:
      if (solving) {
        freeSolutions();
        solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
        glutSetWindowTitle("glpuzzle");
        movingPiece = 0;
      }
      left_mouse = GL_TRUE;
      sel_piece = selectPiece(mousex, mousey);
      if (computeCoords(sel_piece, mousex, mousey, &selx, &sely)) {
        grabPiece(sel_piece, selx, sely);
      }
      glutPostRedisplay();
      break;
    case GLUT_MIDDLE_BUTTON:
      middle_mouse = GL_TRUE;
      glutPostRedisplay();
      break;
    }
  } else {
    switch (b) {
    case GLUT_LEFT_BUTTON:
      left_mouse = GL_FALSE;
      dropSelection();
      glutPostRedisplay();
      break;
    case GLUT_MIDDLE_BUTTON:
      middle_mouse = GL_FALSE;
      glutPostRedisplay();
      break;
    }
  }
  motion(x, y);
}
Example #6
0
bool GameScreen::select(sf::Vector2i s)
{
	if(m_selectedPiece != NULL_SQUARE) //if a piece is selected
	{
		if(m_selectedTarget != NULL_SQUARE) //if an enemy piece is selected
		{
			if(!m_highlighter.isHighlighted(s)) //square isn't selectable
				unselect();
			else //square is selectable
			{
				bool res = m_game.displace(toSquare(m_selectedPiece), toSquare(s), toSquare(m_selectedTarget), false);
				unselect();
				return res;
			}
		}
		else //no enemy piece selected
		{
			if(!m_highlighter.isHighlighted(s)) //square isn't selectable
				unselect();
			else //square is selectable
			{
				PiecePtr p = m_game[toSquare(s)];
				if (p == nullptr) //no piece here
				{
					bool res = m_game.move(toSquare(m_selectedPiece), toSquare(s), false);
					unselect();
					return res;
				}
				else
					selectTarget(s);
			}
		}
	}
	else //no piece is selected
	{
		PiecePtr p = m_game[toSquare(s)];
		if (p != nullptr && p->getColor() == m_game.getActivePlayer())  //there is a piece and it can be selected
			selectPiece(s);
	}
	return false;
}
Example #7
0
int Puzzle_Window::handle(int event) {
  int x = Fl::event_x();
  int y = Fl::event_y();
  switch (event) {
  case FL_KEY:
    switch (Fl::event_key()) {
    case FL_Escape:
    case 'Q':
    case 'q':
      exit(0);
      break;
    case 'S':
    case 's':
      solve_cb(this,0);
      break;
    case 'D':
    case 'd':
      piece = selectPiece(x, y);
      delete_cb(this,0);
      break;
    case 'R':
    case 'r':
      reset_cb(this,0);
      break;
    case 'O':
    case 'o':
      reset_view_cb(this,0);
      break;
    default:
      break;
    }
    return 1;

  case FL_PUSH:
    mousex = curX = x;
    mousey = curY = y;
    switch (Fl::event_button()) {
    case 1:
      set_solving(0);
      left_mouse = true;
      sel_piece = selectPiece(mousex, mousey);
      if (!sel_piece) {
	left_mouse = false;
	middle_mouse = true; // let it rotate object
      } else {
	float selx, sely;
	if (computeCoords(sel_piece, mousex, mousey, &selx, &sely)) {
	  grabPiece(sel_piece, selx, sely);
	}
      }
      redraw();
      break;
    case 2:
      middle_mouse = true;
      redraw();
      break;
    default:
      piece = selectPiece(x, y);
      if (piece) menu->child(2)->activate(); else menu->child(2)->deactivate();
      menu->popup(x, y);
      return 1;
    }
    // fall through to drag handler:

  case FL_DRAG:
    if (middle_mouse && !left_mouse) {
      if (mousex != x || mousey != y) {
	trackball(lastquat,
		  (2.0*mousex - W) / W,
		  (H - 2.0*mousey) / H,
		  (2.0*x - W) / W,
		  (H - 2.0*y) / H);
	spinning = 1;
      } else {
	spinning = 0;
      }
      changeState();
    } else {
      float selx, sely;
      computeCoords(sel_piece, x, y, &selx, &sely);
      moveSelection(selx, sely);
    }
    mousex = x;
    mousey = y;
    redraw();
    return 1;

  case FL_RELEASE:
    if (left_mouse) {
      left_mouse = false;
      dropSelection();
      redraw();
    } else if (middle_mouse) {
      middle_mouse = GL_FALSE;
      redraw();
    }
    return 1;

  }
  return Fl_Gl_Window::handle(event);
}
void tutorial() {
    printf("Hnefatafl (also called Viking chess, King's Table, etc.) is a board game\n");
    printf("with two unequal sides; the attackers, whose goal is to capture the king\n");
    printf("by surrounding him, and the defenders, whose goal is to get their king to\n");
    printf("safety by moving him to one of the exits situated in each corner.\n\n");
    
    pauseForKeyPress();
    
    printf("The players alternate turns of moving a single piece. The pieces can move\n");
    printf("like rooks in chess, and capture by... you know what, it'd be easier to\n");
    printf("just show you.\n\n");
    
    pauseForKeyPress();
    
    TILE board[121];
    TILE a = {0}, b;
    int r;
    initTutorialBoard(board, 0);
    system("cls");
    jumpTo(0,17);
    printf("Select the piece on the board and move it horizontally or vertically.\n");
    printf("You can move the cursor with the arrow keys or W, A, S and D, and you can\n");
    printf("select a space on the board using the spacebar or Enter button.\n\n");
    printf("Note how the piece lights up with a more intense white once selected.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 1, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[60].piece);
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 1);
    jumpTo(0,17);
    printf("Good! "); pauseForKeyPress();
    printf("Now it's time to learn how to capture. Pieces in this game can\n");
    printf("be captured if they are surrounded on two opposite sides. Move the\n");
    printf("attacking piece in place in order to capture the defending piece.\n\n");
    printf("If you select the wrong piece by accident, you can select it again to\n");
    printf("remove the selection.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 1, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[60].piece);
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 2);
    jumpTo(0,17);
    printf("There you go! "); pauseForKeyPress();
    printf("However, if a player voluntarily moves his own piece into a situation\n");
    printf("where it is surrounded, the piece isn't captured.\n\n");
    printf("Move the defending piece in between the two attacking ones to observe this.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 2, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[47].piece != 2);
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 3);
    jumpTo(0,17);
    printf("Well done! Notice how the piece wasn't captured. "); pauseForKeyPress();
    printf("The king needs to be surrounded by four pieces to be captured. If\n");
    printf("the attackers capture the king, they win the game.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 1, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[82].piece);
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 4);
    jumpTo(0,17);
    printf("Success! "); pauseForKeyPress();
    printf("In the event that the king is up against the wall, he only needs to\n");
    printf("be surrounded on the three available tiles around him.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 1, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[39].piece);
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 5);
    jumpTo(0,17);
    printf("Great! Next up, special tiles. "); pauseForKeyPress();
    printf("There are four exits in the game, one in each corner of the board. The\n");
    printf("goal of the defending player is to get the king to one of these exits.\n");
    printf("If the king gets to safety, the defenders win the game.\n\n");
    printf("Move the king to one of the exits. You need to reselect it for each move.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 2, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (!(getIndex(b)==0 || getIndex(b)==10 || getIndex(b)==10*11 || getIndex(b)==10*11+10));
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 6);
    jumpTo(0,17);
    printf("Good job! "); pauseForKeyPress();
    printf("Only the king can be moved to the exits. In addition, the center spot\n");
    printf("where the king is positioned at the beginning of the game can also only\n");
    printf("be occupied by the king.\n\n");
    printf("If you try to move the other piece onto the home tile or one of the exit\n");
    printf("tiles, you'll see that this isn't allowed. Move the king to the home tile\n");
    printf("to proceed.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 2, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (!(getIndex(b)==60));
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 7);
    jumpTo(0,17);
    printf("Fantastic! "); pauseForKeyPress();
    printf("Furthermore, the exits and the home tile can be used as capture partners.\n");
    printf("In other words, a piece standing next to one of these tiles is extra\n");
    printf("vulnerable because it's like being half surrounded already.\n\n");
    printf("Capture the defending soldier and then the king to proceed.");
    do {
        jumpTo(0,1);
        printBoard(board);
        a = selectPiece(board, 1, a);
        printIntense(a);
        b = selectNewPosition(board, a, &r);
        if (r) continue;
        makeMove(a, b, board);
        a = b;
    } while (board[6*11+5].piece || !(board[9*11+9].piece && board[10*11+8].piece));
    
    system("cls");
    jumpTo(0,1);
    printBoard(board);
    initTutorialBoard(board, 7);
    jumpTo(0,17);
    printf("Congratulations! This marks the end of the tutorial. Please remember,\n");
    printf("however, that when you're playing the actual game, it won't constantly be\n");
    printf("your turn (duh). You also won't be denied the opportunity of making bad\n");
    printf("decisions. So think before you move, m'kay?\n\n");
    
    pauseForKeyPress();
}
Example #9
0
int main(int argc, char *argv[])
{
  {
    int i;

    if((argc - 1) % 2)
      goto help;

    for(i = 1; i < argc - 1; i += 2)
      if(!editing && (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--edit")))
	editing = argv[i+1];
      else if(!layout_title[0] && (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--title")))
      {
	bzero(layout_title, 55);
	strncpy(layout_title, argv[i+1], 54);
      }
      else
      {
help:
	printf("usage: mahjong [--edit <layout> [--title <layout title>]]\n");
	return 0;
      }
  }

  srand(time(NULL));
  allegro_init();

  {
    int x, y, z;
    for(x = 16; x--;)
    for(y =  9; y--;)
    for(z =  3; z--;)
      pieceInit(board[x][y][z], x, y, z);
  }

  set_color_depth(SCREEN_DEPTH);

  if(set_gfx_mode(GFX_AUTODETECT_WINDOWED,
      SCREEN_WIDTH,   SCREEN_HEIGHT,
      SCREEN_WIDTH*2, SCREEN_HEIGHT) < 0)
  {
    fprintf(stderr, "fatal: %s\n", allegro_error);
    exit(1);
  }

#ifdef ALLEGRO_WINDOWS
  set_display_switch_callback(SWITCH_IN, update);
#endif

  left_view =
    create_sub_bitmap(screen, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
  right_view =
    create_sub_bitmap(screen, SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

  // init colors
    BACKGROUND_COLOR = makecol32(0x2F, 0x5F, 0x2F); // soft green
      SELECTED_COLOR = makecol32(0x00, 0xDF, 0x00); // green
  OLD_SELECTED_COLOR = makecol32(0x00, 0xBF, 0xBF); // cyan

  // load data
  {
    DATAFILE *data = load_datafile("#");
    new_game   = data[NEW_GAME_BMP].dat;
    undo       = data[UNDO_BMP].dat;
    help       = data[HELP_BMP].dat;
    quit       = data[QUIT_BMP].dat;
    tile       = data[TILE_BMP].dat;
    number[ 0] = data[BLACK1_BMP].dat;
    number[ 2] = data[BLACK2_BMP].dat;
    number[ 4] = data[BLACK3_BMP].dat;
    number[ 6] = data[BLACK4_BMP].dat;
    number[ 8] = data[BLACK5_BMP].dat;
    number[10] = data[BLACK6_BMP].dat;
    number[12] = data[BLACK7_BMP].dat;
    number[14] = data[BLACK8_BMP].dat;
    number[16] = data[BLACK9_BMP].dat;
    number[ 1] = data[RED1_BMP].dat;
    number[ 3] = data[RED2_BMP].dat;
    number[ 5] = data[RED3_BMP].dat;
    number[ 7] = data[RED4_BMP].dat;
    number[ 9] = data[RED5_BMP].dat;
    number[11] = data[RED6_BMP].dat;
    number[13] = data[RED7_BMP].dat;
    number[15] = data[RED8_BMP].dat;
    number[17] = data[RED9_BMP].dat;
    suit[0]    = data[SPADE_BMP].dat;
    suit[1]    = data[CLUB_BMP].dat;
    suit[2]    = data[DIAMOND_BMP].dat;
    suit[3]    = data[HEART_BMP].dat;
    layouts[0] = data[BLOCK_LYT].dat;
    layouts[1] = data[FLAT_LYT].dat;
    layouts[2] = data[FROGGER_LYT].dat;
    layouts[3] = data[PRECIOUS_LYT].dat;
    layouts[4] = data[PTRAD_LYT].dat;
    layouts[5] = data[PYRAMID_LYT].dat;
    layouts[6] = data[STEPS_LYT].dat;
    layouts[7] = data[THETA_LYT].dat;
  }

  scroll_screen(SCREEN_WIDTH, 0);
  current_view = right_view;

  install_timer();
  install_mouse();
  install_keyboard();
  show_mouse(current_view);

  text_mode(BACKGROUND_COLOR);

  if(!editing)
  {
    defaultLayout();

    if(alert("Our Own Version of Mahjong Solitaire, v0.1.4", NULL,
	     "Copyright (c) 2001 Eric Mulvaney, Michelle Bondy",
	     "Play", "Edit", 0, 0) == 2
	&& file_select_ex("Please select layout file to edit:", path, "lyt",
	    PATH_LENGTH-1, OLD_FILESEL_WIDTH, OLD_FILESEL_HEIGHT))
    {
      int x, y, z;

      editing = path;

      for(x = 16; x--;)
      for(y =  9; y--;)
      for(z =  3; z--;)
	board[x][y][z].value = EMPTY;
    }
  }

  mouse_callback = editing ? mouse_handler_for_editing : mouse_handler;

  if(editing)
  {
    Layout data;
    FILE *file = fopen(editing, "r");

    if(file)
    {
      if(fread(&data, sizeof(Layout), 1, file))
      {
	int x, y, z;

	if(!layout_title[0])
	  memcpy(layout_title, data.title, 55);

	for(x = 16; x--;)
	for(y =  9; y--;)
	for(z = (data.board[x][y] > 3) ? 3 : data.board[x][y]; z--;)
	{
	  board[x][y][z].value = BLANK;
	  if(!--n_pieces_left)
	    goto skip;
	}
      }
skip:
      fclose(file);
    }

    update();
  }

  click_ready = 0;
  while(1) // game loop
  {
    if(click_ready)
    {
      int x = click_x - (BOARD_XOFF - 2 * EDGE_WIDTH );
      int y = click_y - (BOARD_YOFF - 2 * EDGE_HEIGHT);
      int z;

      for(z = 3; x > 0 && y > 0 && z--; x -= EDGE_WIDTH, y -= EDGE_HEIGHT)
      {
	int i = x / FACE_WIDTH;
	int j = y / FACE_HEIGHT;

	if(i >= 16 || j >= 9)
	  continue;

	if(editing)
	{
	  if(click_ready == 1 && board[i][j][z].value == EMPTY)
	  {
	    if((z == 0 || board[i][j][z-1].value != EMPTY) && n_pieces_left)
	    {
	      n_pieces_left--;
	      board[i][j][z].value = BLANK;
	      updatePiece(&board[i][j][z]);
	      goto event_handled;
	    }
	  }
	  else if(click_ready == 2 && board[i][j][z].value != EMPTY)
	  {
	    if(z == 2 || board[i][j][z+1].value == EMPTY)
	    {
	      board[i][j][z].value = EMPTY;
	      n_pieces_left++;
	      updatePiece(&board[i][j][z]);
	      goto event_handled;
	    }
	  }
	}
	else if(selectPiece(&board[i][j][z]))
	{
	  if(!n_pairs_remaining)
	  {
	    if(current_view != left_view)
	      update();

	    if(alert("Congratulations!	You won!",
		     "Play another?",
		     NULL, "Yes", "No", 0, 0) == 1)
	      newGame();
	    else
	      return 0;
	  }

	  goto event_handled;
	}
      }

      if(click_y < BUTTON_YOFF + BUTTON_HEIGHT && click_y > BUTTON_YOFF)
      {
	if(editing)
	{
	  if(click_x > NEW_GAME_BUTTON_X1 && click_x < NEW_GAME_BUTTON_X2)
	  {
	    if(n_pieces_left == 144)
	      goto event_handled;

	    if(current_view != left_view)
	      update();

	    if(alert("Are you sure you want to clear the current Layout?",
		     NULL, NULL, "Yes", "No", 0, 0) == 1)
	    {
	      int x, y, z;
	      for(x = 16; x--;)
	      for(y =  9; y--;)
	      for(z =  3; z--;)
		board[x][y][z].value = EMPTY;
	      n_pieces_left = 144;
	      update();
	    }
	  }
	  else if(click_x > QUIT_BUTTON_X1 && click_x < QUIT_BUTTON_X2)
	  {
	    int ync;

	    if(current_view != left_view)
	      update();

	    if(n_pieces_left)
	      ync = alert3("WARNING: Layout is incomplete.",
			   NULL,
			   "Do you wish to save before exiting?",
			   "Yes", "No", "Cancel", 0, 0, 0);
	    else
	      ync = alert3("Do you wish to save before exiting?",
		    NULL, NULL, "Yes", "No", "Cancel", 0, 0, 0);

	    if(ync == 2)
	      return 0;
	    else if(ync == 1)
	    {
	      Layout data;
	      FILE *file;

	      memcpy(data.title, layout_title, 55);

	      data.complete = (n_pieces_left) ? 0 : 1;

	      if((file = fopen(editing, "w")))
	      {
		for(x = 16; x--;)
		for(y =  9; y--;)
		{
		  if	 (board[x][y][2].value == BLANK) data.board[x][y] = 3;
		  else if(board[x][y][1].value == BLANK) data.board[x][y] = 2;
		  else if(board[x][y][0].value == BLANK) data.board[x][y] = 1;
		  else					 data.board[x][y] = 0;
		}

		if(fwrite(&data, sizeof(Layout), 1, file))
		{
		  fclose(file);
		  return 0;
		}
		else
		  fclose(file);
	      }

	      if(alert("WARNING: Save failed!",
		       NULL,
		       "Do you still wish to exit?",
		       "Yes", "No", 0, 0) == 1)
		return 0;
	    }
	  }
	}
	else if(click_x > NEW_GAME_BUTTON_X1 && click_x < NEW_GAME_BUTTON_X2)
	  newGame();
	else if(click_x > UNDO_BUTTON_X1 && click_x < UNDO_BUTTON_X2)
	  undoMove();
	else if(click_x > HELP_BUTTON_X1 && click_x < HELP_BUTTON_X2)
	  giveHelp(1);
	else if(click_x > QUIT_BUTTON_X1 && click_x < QUIT_BUTTON_X2)
	{
	  if(current_view != left_view)
	    update();

	  if(alert("Are you sure you want to quit?",
	      NULL, NULL, "Yes", "No", 0, 0) == 1)
	    return 0;
	}
      }

event_handled:

      click_ready = 0;
    }
    else
      rest(100);
  }

  return 0;
}
Example #10
0
void shuffle()
{
  int x, y, z, i, j, k, pairs[72];
  Piece *edges[144]; int n_edges;

top:
  n_edges = 0;

  // clean the board.
  for(x = 16; x--;)
  for(y =  9; y--;)
  for(z =  3; z--;)
    if(board[x][y][z].value != EMPTY)
      board[x][y][z].value = BLANK;

  // find the edges of the board
  for(y = 9; y--;)
  {
    for(z = 3; z--;) if(board[ 0][y][z].value == BLANK)
      { (edges[n_edges++] = &board[ 0][y][z])->value = EDGE; break; }
    for(z = 3; z--;) if(board[15][y][z].value == BLANK)
      { (edges[n_edges++] = &board[15][y][z])->value = EDGE; break; }
  }

  for(x = 15; --x;)
  for(y =  9; y--;)
  for(z =  3; z--;)
    if(board[x][y][z].value == BLANK)
    {
      if(board[x-1][y][z].value == EMPTY || board[x+1][y][z].value == EMPTY)
	(edges[n_edges++] = &board[x][y][z])->value = EDGE;
      break;
    }

  // shuffle the tiles in pairs
  for(i = 72; i--;)
    pairs[i] = i << 1;
  for(i = 72; i--;)
  {
    int temp = pairs[i];
    pairs[i] = pairs[j = rand() % 72];
    pairs[j] = temp;
  }

  // place the tiles
  for(i = 72; i--;)
  {
    int n = n_edges;
    for(j =  2; j--;)
    {
      Piece *edge;

      if(!n)
	goto top;

      edge     = edges[k = rand() % n];
      edges[k] = edges[--n];

      edge->value = pairs[i]++;

      x = edge->x;
      y = edge->y;
      z = edge->z;

      if(x > 0
	  && board[x-1][y][z].value == BLANK
	  && (z == 2 || board[x-1][y][z+1].value < BLANK))
	(edges[n++] = &board[x-1][y][z])->value = EDGE;

      if(x < 15
	  && board[x+1][y][z].value == BLANK
	  && (z == 2 || board[x+1][y][z+1].value < BLANK))
	(edges[n++] = &board[x+1][y][z])->value = EDGE;

      if(z > 0 && (x == 0 || x == 15
	  || board[x-1][y][z-1].value < BLANK
	  || board[x+1][y][z-1].value < BLANK))
      (edges[n++] = &board[x][y][z-1])->value = EDGE;
    }
    n_edges = n;
  }

  n_moves = 0;
  n_pairs_remaining = 72;
  selectPiece(NULL);
  update();
}