/***************************************************************************
*             __________               __   ___.
*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
*                     \/            \/     \/    \/            \/
*
* Copyright (C) 2005 Adam Boot
*
* Color graphics from Frozen Bubble (http://www.frozen-bubble.org/)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/

#include "plugin.h"
#include "fixedpoint.h"

#include "lib/xlcd.h"
#include "lib/pluginlib_actions.h"
#include "lib/playback_control.h"
#include "lib/highscore.h"

/* files */
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/bubbles.score"
#define SAVE_FILE  PLUGIN_GAMES_DATA_DIR "/bubbles.save"
#define DATA_FILE  PLUGIN_GAMES_DATA_DIR "/bubbles.data"

/* final game return status */
enum {
    BB_LOSE,
    BB_QUIT_WITHOUT_SAVING,
    BB_QUIT,
    BB_USB,
    BB_END,
    BB_WIN,
    BB_NONE,
};

/* play board dimension */
#define BB_HEIGHT 12
#define BB_WIDTH  8
#define BB_LEVEL_HEIGHT 10

/* various amounts */
#define NUM_SCORES   5
#define NUM_LEVELS   100
#define NUM_QUEUE    2
#define NUM_BUBBLES  8
#define MIN_ANGLE    -76
#define MAX_ANGLE    76
#define NUM_COMPRESS 9
#define MAX_SHOTTIME 1000

/* keyboard layouts */

#ifdef HAVE_SCROLLWHEEL
/* sansas use the wheel instead of left/right if available */
#define BUBBLES_LEFT        PLA_SCROLL_BACK
#define BUBBLES_LEFT_REP    PLA_SCROLL_BACK_REPEAT
#define BUBBLES_RIGHT       PLA_SCROLL_FWD
#define BUBBLES_RIGHT_REP   PLA_SCROLL_FWD_REPEAT
#else
#define BUBBLES_LEFT        PLA_LEFT
#define BUBBLES_LEFT_REP    PLA_LEFT_REPEAT
#define BUBBLES_RIGHT       PLA_RIGHT
#define BUBBLES_RIGHT_REP   PLA_RIGHT_REPEAT
#endif

#define ANGLE_STEP          2
#define ANGLE_STEP_REP      6

#define BUBBLES_QUIT1       PLA_EXIT

/* these are better off shooting with up */
#if (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) \
 || (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) \
 || (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define SHOOT_WITH_UP
#endif

#ifdef SHOOT_WITH_UP
#define BUBBLES_FIRE        PLA_UP
#define BUBBLES_FIRE_REPEAT PLA_UP_REPEAT
#define BUBBLES_PAUSE       PLA_SELECT
#define BUBBLES_QUIT2       PLA_CANCEL
#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
    || (CONFIG_KEYPAD == IPOD_3G_PAD) \
    || (CONFIG_KEYPAD == IPOD_4G_PAD)
#define BUBBLES_FIRE        PLA_SELECT
#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
#define BUBBLES_PAUSE       PLA_DOWN
#define BUBBLES_QUIT2       PLA_UP
#else
#define BUBBLES_FIRE        PLA_SELECT
#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
#define BUBBLES_PAUSE       PLA_UP
#define BUBBLES_QUIT2       PLA_CANCEL
#endif

/* external bitmaps */
#ifdef HAVE_LCD_COLOR
#include "pluginbitmaps/bubbles_background.h"
#endif
#include "pluginbitmaps/bubbles_bubble.h"
#include "pluginbitmaps/bubbles_emblem.h"

#define BUBBLE_WIDTH  BMPWIDTH_bubbles_bubble
#define BUBBLE_HEIGHT BMPHEIGHT_bubbles_bubble
#define EMBLEM_WIDTH  BMPWIDTH_bubbles_emblem
#define EMBLEM_HEIGHT (BMPHEIGHT_bubbles_emblem/8)

/* bubbles will consume height of ROW_HEIGHT*(BB_HEIGHT-1)+BUBBLE_HEIGHT*3/2 */
/* 44x44 bubbles (m:robe 500) */
#if (LCD_WIDTH == 640) && (LCD_HEIGHT == 480)
#define XOFS          144
#define MAX_FPS       40

/* custom text positioning */
#define LEVEL_TXT_X     58
#define LEVEL_TXT_WIDTH 32
#define LEVEL_TXT_Y     8
#define SCORE_TXT_X     58
#define SCORE_TXT_WIDTH 32
#define SCORE_TXT_Y     62
#define NEXT_BB_X       58
#define NEXT_BB_WIDTH   32
#define NEXT_BB_Y       402

#elif (LCD_WIDTH == 480) && (LCD_HEIGHT >= 640)
#define XOFS          128
#define MAX_FPS       40

#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
#define XOFS            86
#define YOFS            100
#define SCORE_TXT_X     28
#define SCORE_TXT_WIDTH 32
#define SCORE_TXT_Y     48
#define LEVEL_TXT_X     27
#define LEVEL_TXT_WIDTH 32
#define LEVEL_TXT_Y     5
#define MAX_FPS         40

/* 22x22 bubbles (iPod Video) */
#elif (LCD_HEIGHT == 240) && (LCD_WIDTH == 320)
#define XOFS          72
#define MAX_FPS       40

/* custom text positioning */
#define NEXT_BB_X       20
#define NEXT_BB_WIDTH   32
#define NEXT_BB_Y       200

/* 22x22 bubbles (Gigabeat, Onda VX747) */
#elif ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)) && (LCD_WIDTH == 240)
#define XOFS          64
#define MAX_FPS       30

/* custom text positioning */
#define LEVEL_TXT_X     21
#define LEVEL_TXT_WIDTH 32
#define LEVEL_TXT_Y     2
#define SCORE_TXT_X     20
#define SCORE_TXT_WIDTH 32
#define SCORE_TXT_Y     29
#define NEXT_BB_X       20
#define NEXT_BB_WIDTH   32
#define NEXT_BB_Y       200

/* 22x22 bubbles (Anbernic RG Nano) */
#elif (LCD_HEIGHT == 240) && (LCD_WIDTH == 240)
#define XOFS            64
#define MAX_FPS         40

/* custom text positioning */
#define SCORE_TXT_X     12
#define SCORE_TXT_WIDTH 32
#define SCORE_TXT_Y     29
#define LEVEL_TXT_X     12
#define LEVEL_TXT_WIDTH 32
#define LEVEL_TXT_Y     2
#define NEXT_BB_X       12
#define NEXT_BB_WIDTH   32
#define NEXT_BB_Y       200

/* 16x16 bubbles (H300, iPod Color, HDD6330) */
#elif (LCD_HEIGHT == 176) && (LCD_WIDTH == 220)
#define XOFS          46
#define MAX_FPS       30

/* 16x16 bubbles (Sansa E200) */
#elif (LCD_HEIGHT == 220) && (LCD_WIDTH == 176)
#define XOFS            24
#define MAX_FPS         30
#define YOFS            45

/* custom text positioning */
#define LEVEL_TXT_X     24
#define LEVEL_TXT_WIDTH 31
#define LEVEL_TXT_Y      5
#define SCORE_TXT_X     58
#define SCORE_TXT_WIDTH 31
#define SCORE_TXT_Y      5
#define NEXT_BB_X      112
#define NEXT_BB_WIDTH   31
#define NEXT_BB_Y        3

/* 12x12 bubbles (iPod Nano) */
#elif (LCD_HEIGHT == 132) && (LCD_WIDTH == 176)
#define XOFS          40
#define MAX_FPS       40

/* 12x12 bubbles (H100, H10, iAudio X5, HDD1630, iPod 3G, iPod 4G grayscale) */
#elif (LCD_HEIGHT == 128) && ((LCD_WIDTH == 160) || (LCD_WIDTH == 128))
#define XOFS          33
#define MAX_FPS       30

/* 12x12 bubbles (GoGear SA9200) */
#elif (LCD_HEIGHT == 160) && (LCD_WIDTH == 128)
#define XOFS          16
#define YOFS          32
#define ROW_HEIGHT    10
#define ROW_INDENT     6
#define MAX_FPS       30

/* custom text positioning */
#define LEVEL_TXT_X      2
#define LEVEL_TXT_WIDTH 31
#define LEVEL_TXT_Y      3
#define SCORE_TXT_X     34
#define SCORE_TXT_WIDTH 31
#define SCORE_TXT_Y      3
#define NEXT_BB_X       81
#define NEXT_BB_WIDTH   31
#define NEXT_BB_Y        2

/* 10x10 bubbles (iPod Mini) */
#elif (LCD_HEIGHT == 110) && (LCD_WIDTH == 138)
#define XOFS          33
#define MAX_FPS       30

/* 9x9 bubbles (iAudio M3, Samsung YH-820) */
#elif (LCD_HEIGHT == 96) && (LCD_WIDTH == 128)
#define XOFS          29
#define MAX_FPS       30

/* 8x8 bubbles (Sansa C200) */
#elif ((LCD_HEIGHT == 80) && (LCD_WIDTH == 132))
#define XOFS          45
#define ROW_HEIGHT     6
#define MAX_FPS       30

/* 7x7 bubbles (Sansa Clip/m200) */
#elif (LCD_HEIGHT == 64 && LCD_WIDTH == 128)
#define XOFS          33
#define ROW_HEIGHT     5
#define MAX_FPS       30

/* 8x7 bubbles (Archos recorder, Ondio) */
#elif (LCD_HEIGHT == 64) && (LCD_WIDTH == 112)
#define XOFS          33
#define ROW_HEIGHT     5
#define MAX_FPS       20

/* 7x7 bubbles (Sansa Clip Zip)  */
#elif (LCD_HEIGHT == 96 && LCD_WIDTH == 96)
#define XOFS          33
#define ROW_HEIGHT     5
#define MAX_FPS       30

/* custom text positioning */
#define LEVEL_TXT_X      1
#define LEVEL_TXT_WIDTH 31
#define LEVEL_TXT_Y      4
#define SCORE_TXT_X      1
#define SCORE_TXT_WIDTH 31
#define SCORE_TXT_Y     31
#define NEXT_BB_X        1
#define NEXT_BB_WIDTH   31
#define NEXT_BB_Y       72

#else
    #error BUBBLES: Unsupported LCD type
#endif

#if !defined(ROW_HEIGHT)
#define ROW_HEIGHT    (BUBBLE_WIDTH-(BUBBLE_WIDTH-EMBLEM_WIDTH)/2)
#endif

#if !defined(ROW_INDENT)
#define ROW_INDENT    (BUBBLE_WIDTH/2)
#endif

#define TEXT_LINES (LCD_HEIGHT/8)

#ifndef YOFS
#define YOFS 0
#endif

/* shot position */
#define SHOTX XOFS+ROW_INDENT+BUBBLE_WIDTH*3
#define SHOTY (YOFS+ROW_HEIGHT*(BB_HEIGHT-1)+BUBBLE_HEIGHT/2)

/* collision distance squared */
#define MIN_DISTANCE ((BUBBLE_WIDTH*8)/10)*((BUBBLE_HEIGHT*8)/10)

/* levels */
char level[NUM_LEVELS][BB_LEVEL_HEIGHT][BB_WIDTH] = {
    {{ 6,  6,  4,  4,  2,  2,  3,  3},
     { 6,  6,  4,  4,  2,  2,  3, -1},
     { 2,  2,  3,  3,  6,  6,  4,  4},
     { 2,  3,  3,  6,  6,  4,  4, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  7,  7,  7,  7,  7,  7, -1},
     {-1,  1,  1,  1,  1,  1, -1, -1},
     {-1, -1,  2,  2,  2,  2, -1, -1},
     {-1, -1, -1,  2, -1, -1, -1, -1},
     {-1, -1, -1,  2,  2, -1, -1, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  7, -1, -1,  7, -1, -1},
     {-1, -1,  7,  1,  7, -1, -1, -1},
     {-1, -1, -1,  1,  2, -1, -1, -1},
     {-1, -1,  1,  2,  1, -1, -1, -1},
     {-1, -1, -1,  2,  5, -1, -1, -1},
     {-1, -1,  3,  5,  3, -1, -1, -1},
     {-1, -1, -1,  5,  3, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  0,  0, -1, -1, -1},
     {-1, -1,  5,  0,  1, -1, -1, -1},
     {-1, -1,  3,  5,  1,  6, -1, -1},
     {-1,  4,  3, -1,  6,  7, -1, -1},
     {-1,  7,  4, -1, -1,  7,  4, -1},
     { 6,  7, -1, -1, -1,  4,  3, -1},
     { 1,  6, -1, -1, -1, -1,  3,  5},
     { 1, -1, -1, -1, -1, -1,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  0,  0,  0,  0, -1, -1},
     {-1,  0,  1,  1,  1,  0, -1, -1},
     {-1,  0,  1,  0,  0,  1,  0, -1},
     {-1,  0,  1,  1,  1,  0, -1, -1},
     {-1, -1,  0,  0,  0,  0, -1, -1},
     {-1, -1,  7, -1,  7, -1, -1, -1},
     {-1, -1,  7,  7,  7,  7, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  4,  4,  4,  6,  6,  6, -1},
     { 4, -1, -1, -1, -1, -1,  6, -1},
     {-1,  4, -1, -1, -1, -1,  6, -1},
     { 4,  2,  3,  1,  2,  3,  6, -1},
     {-1,  3,  1,  2,  3,  1,  2, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  4,  4,  4,  6,  6,  6, -1},
     { 4, -1, -1, -1, -1, -1,  6, -1},
     {-1,  4, -1, -1, -1, -1,  6, -1},
     { 4,  2,  3,  1,  2,  3,  6, -1},
     {-1,  3,  1,  2,  3,  1,  2, -1},
     {-1,  2,  3,  1,  2,  3, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  0,  0, -1, -1,  2,  2, -1},
     {-1,  5, -1, -1, -1,  3, -1, -1},
     {-1,  0, -1, -1, -1,  6, -1, -1},
     {-1,  3, -1, -1, -1,  0, -1, -1},
     {-1,  4, -1, -1, -1,  5, -1, -1},
     {-1,  2, -1, -1, -1,  3, -1, -1},
     {-1,  2, -1, -1, -1,  1, -1, -1},
     {-1,  3, -1, -1, -1,  4, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 3, -1, -1, -1, -1, -1, -1,  3},
     { 6,  3,  2,  4,  6,  3,  2, -1},
     { 4, -1, -1, -1, -1, -1, -1,  4},
     { 2,  4,  6,  3,  2,  4,  6, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  2, -1,  1, -1,  1, -1,  2},
     { 1,  2, -1,  2,  1, -1,  1, -1},
     { 1, -1,  1, -1,  2, -1,  2, -1},
     { 2,  1, -1,  1,  2, -1,  2, -1},
     {-1,  2, -1,  2, -1,  2, -1,  2},
     { 1,  2, -1,  2,  1, -1,  1, -1},
     { 1, -1,  1, -1,  2, -1,  1, -1},
     { 2,  2, -1,  1,  1, -1,  2, -1},
     {-1,  2, -1,  1, -1,  1, -1,  1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  7,  7, -1, -1,  5,  5, -1},
     { 1, -1, -1, -1, -1, -1,  4, -1},
     { 2,  1, -1, -1, -1, -1,  4,  3},
     { 2, -1, -1, -1, -1, -1,  3, -1},
     { 1,  2, -1, -1, -1, -1,  3,  4},
     { 1, -1, -1, -1, -1, -1,  4, -1},
     { 7,  1, -1, -1, -1, -1,  4,  5},
     { 7,  7, -1, -1, -1,  5,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 7,  7, -1, -1, -1, -1,  5,  5},
     { 1,  5, -1, -1, -1,  7,  4, -1},
     { 2,  1, -1, -1, -1, -1,  4,  3},
     { 2, -1, -1, -1, -1, -1,  3, -1},
     { 1,  5, -1, -1, -1, -1,  7,  4},
     { 1, -1, -1, -1, -1, -1,  4, -1},
     { 7,  1, -1, -1, -1, -1,  4,  5},
     { 7,  5, -1, -1, -1,  7,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  0,  0, -1, -1, -1},
     {-1, -1,  5,  0,  1, -1, -1, -1},
     {-1, -1,  3,  5,  1,  6, -1, -1},
     {-1,  4,  3,  2,  6,  2, -1, -1},
     {-1,  7,  4,  7,  2,  2,  4, -1},
     { 6,  7,  7,  3,  3,  4,  3, -1},
     { 1,  6,  1,  1,  1,  3,  3,  5},
     { 1,  1, -1, -1, -1, -1,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  0, -1, -1,  0, -1, -1},
     {-1,  3,  3, -1,  3,  3, -1, -1},
     {-1,  0,  2,  0,  0,  2,  0, -1},
     {-1,  3,  3, -1,  3,  3, -1, -1},
     {-1, -1,  0, -1, -1,  0, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  1,  1, -1, -1, -1},
     {-1, -1,  2,  2,  2, -1, -1, -1},
     {-1, -1,  3,  3,  3,  3, -1, -1},
     {-1,  4,  4,  4,  4,  4, -1, -1},
     {-1,  5,  5,  5,  5,  5,  5, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1},
     {-1, -1, -1,  7,  7, -1, -1, -1},
     {-1, -1, -1,  0, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  2,  5, -1, -1, -1},
     {-1,  4,  3, -1, -1, -1, -1, -1},
     { 6,  7, -1,  5,  2, -1, -1, -1},
     {-1, -1, -1, -1,  3,  4, -1, -1},
     {-1, -1, -1,  2,  5, -1,  7,  6},
     {-1,  4,  3, -1, -1, -1, -1, -1},
     { 6,  7, -1,  5,  2, -1, -1, -1},
     {-1, -1, -1, -1,  3,  4, -1, -1},
     {-1, -1, -1, -1, -1, -1,  7,  6},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1,  1, -1, -1, -1, -1},
     {-1, -1, -1,  7, -1, -1, -1, -1},
     {-1, -1, -1,  2, -1, -1, -1, -1},
     {-1, -1, -1,  4, -1, -1, -1, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  0,  1, -1, -1, -1},
     {-1, -1,  0,  2,  7,  7, -1, -1},
     {-1, -1, -1,  0,  1,  7, -1, -1},
     {-1,  0,  0,  0,  0, -1, -1, -1},
     {-1,  0,  0,  0,  1,  1, -1, -1},
     { 0,  0,  0,  1,  1,  1, -1, -1},
     {-1,  0,  0,  1,  1,  1, -1, -1},
     {-1,  0,  0,  0,  7,  7, -1, -1},
     {-1, -1,  7,  7, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  1, -1, -1, -1, -1, -1, -1},
     { 1, -1, -1, -1, -1, -1, -1, -1},
     {-1,  2,  3,  4,  7,  6,  5, -1},
     {-1, -1, -1, -1, -1, -1,  1, -1},
     {-1, -1, -1, -1, -1, -1,  1, -1},
     {-1,  2,  3,  4,  7,  6, -1, -1},
     {-1,  1, -1, -1, -1, -1, -1, -1},
     { 1, -1, -1, -1, -1, -1, -1, -1},
     {-1,  2,  3,  4,  7,  6,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6, -1, -1, -1, -1, -1, -1},
     { 5, -1, -1, -1, -1, -1, -1, -1},
     { 2,  3,  4,  7,  6,  5,  2,  3},
     {-1, -1, -1, -1, -1, -1,  4, -1},
     {-1, -1, -1, -1, -1, -1,  7, -1},
     {-1,  4,  3,  2,  5,  6, -1, -1},
     {-1,  7, -1, -1, -1, -1, -1, -1},
     { 6, -1, -1, -1, -1, -1, -1, -1},
     { 5,  2,  3,  4,  7,  6,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 3,  2,  1,  0,  0,  1,  2,  3},
     { 3,  2,  1,  0,  1,  2,  3, -1},
     { 4,  3,  2,  1,  1,  2,  3,  4},
     { 4,  3,  2,  1,  2,  3,  4, -1},
     { 5,  4,  3,  2,  2,  3,  4,  5},
     { 5,  4,  3,  2,  3,  4,  5, -1},
     { 6,  5,  4,  3,  3,  4,  5,  6},
     { 6,  5,  4,  3,  4,  5,  6, -1},
     { 7,  6,  5,  4,  4,  5,  6,  7},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1,  2,  4, -1, -1, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1},
     {-1, -1, -1,  2,  4, -1, -1, -1},
     {-1,  2, -1,  5, -1,  4, -1, -1},
     { 1,  0,  1,  0,  1,  0,  1,  0},
     { 3, -1,  3, -1,  2, -1,  6, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1,  1, -1, -1, -1},
     { 7,  4,  3,  5, -1, -1, -1, -1},
     { 6, -1, -1,  1, -1, -1, -1, -1},
     {-1, -1, -1,  5,  3,  4,  7, -1},
     { 6, -1, -1, -1,  1, -1, -1,  6},
     { 7,  4,  3,  5, -1, -1, -1, -1},
     {-1, -1, -1,  1, -1, -1, -1,  6},
     {-1, -1, -1,  5,  3,  4,  7, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1,  7,  3,  6, -1},
     {-1, -1,  3,  7,  3,  6,  3, -1},
     {-1, -1,  5,  7,  3,  6,  3, -1},
     {-1,  6,  7,  3,  6,  7, -1, -1},
     {-1,  7,  7,  3,  6,  1, -1, -1},
     { 3,  7,  3,  6,  3, -1, -1, -1},
     { 5,  6,  2,  7,  1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 5, -1, -1, -1, -1, -1, -1,  5},
     { 5, -1,  6,  6,  6, -1,  5, -1},
     {-1,  5,  4, -1, -1,  4,  5, -1},
     {-1,  3, -1, -1, -1,  3, -1, -1},
     {-1,  6,  0, -1, -1,  0,  6, -1},
     {-1,  3, -1, -1, -1,  3, -1, -1},
     {-1, -1,  4, -1, -1,  4, -1, -1},
     {-1, -1,  6,  6,  6, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  7,  0, -1, -1,  0,  7, -1},
     { 7, -1,  0, -1,  0, -1,  7, -1},
     { 7,  1, -1,  0,  0, -1,  1,  7},
     { 7,  1,  2,  0,  2,  1,  7, -1},
     { 7,  6,  3,  2,  2,  3,  6,  7},
     { 7, -1,  3,  2,  3, -1,  7, -1},
     {-1,  7,  7,  3,  3,  7,  7, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  3, -1,  1, -1,  7, -1,  6},
     { 5, -1,  7, -1,  7, -1,  6, -1},
     { 6, -1,  0, -1,  5, -1,  3, -1},
     {-1,  2, -1,  1, -1,  5, -1, -1},
     {-1,  4, -1,  3, -1,  4, -1, -1},
     { 2, -1,  3, -1,  2, -1, -1, -1},
     {-1, -1,  4, -1,  6, -1, -1, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1,  1, -1, -1, -1},
     {-1, -1, -1, -1,  3, -1, -1, -1},
     { 6,  1,  3,  1,  2,  1,  4,  1},
     {-1, -1, -1, -1,  6, -1, -1, -1},
     {-1, -1, -1,  4,  1, -1, -1, -1},
     {-1, -1,  1, -1,  3, -1, -1, -1},
     {-1, -1, -1,  2,  1, -1, -1, -1},
     {-1, -1, -1, -1,  4, -1, -1, -1},
     {-1, -1, -1,  6,  1, -1, -1, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  4, -1, -1, -1},
     {-1, -1,  4,  1,  0, -1, -1, -1},
     {-1, -1, -1,  2,  3, -1, -1, -1},
     {-1,  1,  4, -1,  2,  2, -1, -1},
     {-1,  3,  1,  2,  5,  1,  4, -1},
     {-1,  4,  2, -1,  0,  4, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1,  1, -1, -1, -1},
     {-1, -1, -1,  1, -1, -1, -1, -1},
     {-1,  2, -1, -1,  1, -1,  5, -1},
     { 5, -1, -1,  1, -1, -1,  0, -1},
     {-1,  6, -1, -1,  1, -1,  4, -1},
     {-1,  0, -1,  1, -1,  5, -1, -1},
     {-1, -1,  5,  5,  0,  1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  6,  3, -1, -1, -1},
     {-1, -1,  3,  2,  6, -1, -1, -1},
     {-1, -1,  2,  6,  3,  2, -1, -1},
     {-1,  6,  3,  2,  6,  3, -1, -1},
     {-1,  3,  2,  6,  3,  2,  6, -1},
     { 2,  6,  3,  2,  6,  3,  2, -1},
     { 6,  3,  2,  6,  3,  2,  6,  3},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  6,  6,  6,  6,  6,  6,  6},
     { 4, -1, -1, -1, -1, -1, -1, -1},
     {-1,  3,  2,  5,  7,  6,  4,  3},
     {-1,  5, -1, -1, -1, -1, -1, -1},
     {-1, -1,  7,  6,  4,  3,  2,  5},
     {-1, -1,  4, -1, -1, -1, -1, -1},
     {-1, -1, -1,  3,  2,  5,  7,  6},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1, -1,  7, -1, -1,  6, -1,  2},
     { 6, -1,  1, -1,  6,  1,  3, -1},
     {-1,  4, -1,  7,  2, -1,  7, -1},
     { 2,  7, -1, -1, -1,  4, -1, -1},
     { 6, -1,  3,  5,  0,  2, -1,  7},
     { 1, -1, -1, -1, -1, -1,  1, -1},
     {-1,  1,  4,  5,  7,  5,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  6,  6, -1, -1,  6,  6,  6},
     {-1, -1,  6, -1,  6, -1, -1, -1},
     {-1, -1,  2,  3,  3,  2, -1, -1},
     {-1,  3, -1,  5, -1,  3, -1, -1},
     {-1, -1,  5,  3,  3,  5, -1, -1},
     {-1, -1,  6,  1,  6, -1, -1, -1},
     {-1,  4,  2, -1, -1,  2,  4, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1,  5, -1, -1, -1, -1, -1},
     {-1,  3,  4,  6,  6, -1, -1,  5},
     { 3,  3,  4,  6,  5, -1,  5, -1},
     { 3,  2,  3,  6,  6,  5,  5, -1},
     { 3,  3,  4,  6,  5, -1,  5, -1},
     {-1,  3,  4,  6,  6, -1, -1,  5},
     {-1, -1,  5, -1, -1, -1, -1, -1},
     {-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1, -1, -1, -1, -1, -1, -1,  1},
     { 1, -1,  2,  2,  2, -1,  1, -1},
     {-1,  1,  2,  3,  3,  2,  1, -1},
     { 6,  2,  3, -1,  3,  2,  6, -1},
     { 6,  2,  3, -1, -1,  3,  2,  6},
     { 6,  2,  3, -1,  3,  2,  6, -1},
     { 3,  3,  3,  7,  7,  3,  3,  3},
     { 0,  5,  0,  2,  0,  5,  0, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  7,  7,  7, -1, -1, -1},
     {-1,  7,  2,  2,  7, -1, -1, -1},
     {-1,  7,  5,  5,  5,  7, -1, -1},
     { 7,  7,  7,  7,  7,  7, -1, -1},
     {-1, -1,  6, -1,  6, -1, -1, -1},
     {-1,  6, -1, -1,  6, -1, -1, -1},
     {-1,  6,  4,  4, -1,  6,  4,  4},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  3,  3, -1,  3,  3,  3, -1},
     { 3,  7,  5,  4,  6,  5,  3, -1},
     { 1,  3,  3,  3, -1,  3,  3,  1},
     { 2,  1,  2,  1,  2,  1,  2, -1},
     { 1,  3,  3, -1,  3,  3,  3,  1},
     { 3,  5,  6,  4,  5,  7,  3, -1},
     { 2,  3,  3,  3, -1,  3,  3,  2},
     { 1,  1,  2,  2,  2,  1,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6,  5, -1, -1, -1, -1, -1},
     { 3,  1,  3, -1, -1, -1, -1, -1},
     {-1,  5,  6, -1, -1, -1, -1, -1},
     {-1, -1,  5,  3, -1, -1, -1, -1},
     {-1, -1,  6,  1,  6, -1, -1, -1},
     {-1, -1,  3,  5, -1, -1, -1, -1},
     {-1, -1, -1, -1,  3,  6, -1, -1},
     {-1, -1, -1,  5,  6,  5, -1, -1},
     {-1, -1, -1, -1,  6,  3, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  3,  7,  4,  5,  1,  6,  3},
     { 5,  1,  6,  3,  7,  4,  5, -1},
     { 6,  3,  7,  4,  5,  1,  6,  3},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1, -1, -1,  4,  4},
     {-1, -1,  7,  7,  7,  4,  4, -1},
     {-1, -1, -1, -1, -1, -1,  4,  4},
     {-1,  1, -1, -1, -1,  7, -1, -1},
     {-1,  1,  1, -1, -1,  7, -1, -1},
     { 3,  3,  3, -1,  7, -1, -1, -1},
     { 3, -1,  2,  3,  3,  3, -1,  3},
     {-1,  2, -1,  3, -1,  3,  3, -1},
     {-1,  2, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  4, -1, -1, -1, -1, -1},
     {-1,  7,  4, -1, -1, -1, -1, -1},
     {-1, -1,  7,  4, -1, -1, -1, -1},
     {-1,  4,  7,  4, -1, -1, -1, -1},
     { 1,  1,  1,  1,  1,  1,  1, -1},
     { 1,  2,  1,  2,  1,  1, -1, -1},
     { 2,  2,  2,  2,  2,  2,  2,  2},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 0, -1, -1, -1, -1, -1, -1,  6},
     { 6,  1,  4,  3,  7,  5,  0, -1},
     { 0, -1, -1, -1, -1, -1, -1,  6},
     { 6,  1,  4,  3,  7,  5,  0, -1},
     { 0, -1, -1, -1, -1, -1, -1,  6},
     { 6,  1,  4,  3,  7,  5,  0, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 3,  3,  4,  6,  6,  4,  3,  3},
     { 0,  3,  4,  6,  4,  3,  1, -1},
     { 5,  1,  3,  4,  4,  3,  0,  1},
     { 0,  1,  3,  4,  3,  1,  0, -1},
     { 2,  1,  6,  3,  3,  0,  0,  1},
     { 0,  3,  4,  3,  6,  1,  5, -1},
     { 6,  1,  2,  6,  4,  0,  0,  2},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  6, -1, -1, -1, -1,  4,  4},
     { 4,  0, -1, -1, -1,  3,  6, -1},
     { 0,  6, -1, -1, -1, -1,  4,  2},
     { 7, -1, -1, -1, -1, -1,  7, -1},
     { 4,  4, -1, -1, -1, -1,  5,  6},
     { 6,  4,  7,  7,  5,  6,  4, -1},
     {-1,  7,  6,  4,  6,  4,  7, -1},
     {-1,  0, -1,  7, -1,  7, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  5, -1, -1, -1, -1,  4, -1},
     {-1,  5, -1, -1, -1,  4, -1, -1},
     {-1, -1,  5,  6,  6,  4, -1, -1},
     {-1, -1,  2, -1,  2, -1, -1, -1},
     { 0,  0,  6, -1, -1,  6,  1,  1},
     {-1, -1,  2, -1,  2, -1, -1, -1},
     {-1, -1,  7,  6,  6,  3, -1, -1},
     {-1,  7, -1, -1, -1,  3, -1, -1},
     {-1,  7, -1, -1, -1, -1,  3, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6, -1, -1, -1, -1,  2, -1},
     { 1,  7,  1,  1,  1,  3,  1, -1},
     {-1, -1,  4,  1,  1,  4, -1, -1},
     {-1,  1,  3,  1,  7,  1, -1, -1},
     {-1, -1, -1,  2,  6, -1, -1, -1},
     {-1, -1,  1,  5,  1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 7,  7,  7,  7,  7,  7,  7,  7},
     { 7, -1, -1, -1, -1, -1,  7, -1},
     { 7, -1, -1,  2,  0,  5,  2,  2},
     { 7, -1, -1, -1,  0,  3,  6, -1},
     { 7, -1, -1, -1, -1, -1,  4,  0},
     { 5,  5, -1, -1, -1, -1, -1, -1},
     { 4,  3,  6,  2, -1, -1, -1, -1},
     { 0,  2,  0,  4, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  1, -1, -1,  1, -1, -1},
     {-1,  4, -1, -1,  5, -1, -1, -1},
     {-1,  7, -1, -1,  1,  1,  1, -1},
     { 6, -1, -1, -1, -1,  7, -1, -1},
     { 1,  1,  1,  1, -1,  4, -1, -1},
     {-1, -1,  5, -1, -1, -1, -1, -1},
     {-1, -1,  0, -1, -1, -1, -1, -1},
     {-1,  3, -1, -1, -1, -1, -1, -1},
     {-1,  1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  7,  7, -1, -1,  7,  7, -1},
     { 6, -1,  4, -1,  4, -1,  6, -1},
     { 5, -1, -1,  3,  3, -1, -1,  5},
     { 6, -1, -1, -1, -1, -1,  6, -1},
     {-1,  7, -1, -1, -1, -1,  7, -1},
     {-1,  4, -1, -1, -1,  4, -1, -1},
     {-1, -1,  3, -1, -1,  3, -1, -1},
     {-1, -1,  2, -1,  2, -1, -1, -1},
     {-1, -1, -1,  5,  5, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  0,  0, -1, -1,  0,  0, -1},
     { 7,  4,  6,  6,  6,  4,  3, -1},
     { 5,  6,  6,  6,  2,  6,  6,  3},
     { 7,  4,  6,  6,  6,  4,  3, -1},
     {-1,  0,  0, -1, -1,  0,  0, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1, -1,  7,  7,  7},
     {-1, -1, -1, -1,  2,  7,  7, -1},
     {-1,  0,  7,  7,  7, -1,  7,  7},
     { 6,  7,  7,  7, -1, -1, -1, -1},
     { 6, -1, -1, -1,  7,  7,  7,  7},
     { 6, -1, -1, -1, -1, -1, -1, -1},
     { 4,  2,  2,  2,  4, -1,  3, -1},
     { 4,  4,  4,  4,  3,  3,  3, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 4, -1, -1,  7, -1,  6, -1,  7},
     { 7,  6,  7, -1, -1,  7,  4, -1},
     {-1, -1,  7, -1, -1,  7, -1, -1},
     {-1,  0,  0,  0,  0,  0,  3, -1},
     {-1, -1,  0,  2,  2,  0,  6,  4},
     {-1, -1,  0,  0,  0,  1,  3, -1},
     {-1, -1, -1,  0,  0, -1,  3,  4},
     {-1, -1, -1,  6, -1,  5,  6, -1},
     {-1, -1, -1, -1, -1, -1,  1,  0},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  5, -1, -1, -1, -1,  5, -1},
     { 0, -1, -1,  0, -1, -1,  0, -1},
     { 0,  0,  0,  2,  2,  0,  0,  0},
     { 0, -1, -1,  0, -1, -1,  0, -1},
     {-1,  7, -1,  3, -1, -1,  7, -1},
     {-1, -1,  3,  6, -1, -1, -1, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1},
     {-1,  3,  6, -1, -1, -1, -1, -1},
     {-1,  3, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  6,  5, -1, -1, -1},
     {-1, -1,  2,  6,  3, -1, -1, -1},
     {-1, -1,  5,  4,  7,  1, -1, -1},
     {-1,  6,  2,  2,  3,  4, -1, -1},
     {-1, -1,  3,  7,  3,  6, -1, -1},
     {-1, -1,  1,  3,  2, -1, -1, -1},
     {-1, -1, -1,  4,  5, -1, -1, -1},
     {-1, -1, -1,  4, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 7,  7, -1,  2,  2, -1,  6,  6},
     { 6, -1, -1,  6, -1, -1,  3, -1},
     { 2, -1, -1,  1, -1, -1,  2, -1},
     { 5, -1, -1,  3, -1, -1,  2, -1},
     { 1, -1, -1,  2, -1, -1,  1, -1},
     { 5, -1, -1,  2, -1, -1,  2, -1},
     { 6, -1, -1,  1, -1, -1,  7, -1},
     { 5, -1, -1,  5, -1, -1,  4, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  6,  6, -1, -1, -1},
     {-1,  0,  4,  4,  4,  0, -1, -1},
     {-1, -1, -1,  6,  6, -1, -1, -1},
     {-1, -1,  2,  7,  2, -1, -1, -1},
     {-1, -1, -1,  6,  6, -1, -1, -1},
     {-1,  0,  5,  5,  5,  0, -1, -1},
     {-1, -1, -1,  3,  3, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  4,  1,  3, -1, -1, -1},
     {-1,  1, -1, -1,  1, -1, -1, -1},
     {-1, -1,  4,  1,  3,  4,  1, -1},
     {-1,  1,  3,  4, -1, -1,  4, -1},
     {-1,  3, -1, -1,  3,  4,  1, -1},
     {-1,  1,  3,  4,  1,  3, -1, -1},
     {-1, -1,  4,  1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6,  4, -1,  3,  2,  5, -1},
     { 0, -1, -1, -1, -1, -1,  1, -1},
     {-1,  2,  3,  5, -1,  4,  6, -1},
     { 0, -1, -1, -1, -1, -1,  1, -1},
     {-1,  4,  6, -1,  2,  5,  3, -1},
     { 0, -1, -1, -1, -1, -1,  1, -1},
     {-1,  5,  2,  3, -1,  4,  6, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  6,  6, -1, -1, -1},
     {-1, -1,  7,  6,  4, -1, -1, -1},
     {-1,  2,  1,  7,  4,  1,  3, -1},
     { 2,  1,  1,  1,  1,  1,  3, -1},
     {-1,  2,  2,  2,  3,  3,  3, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1, -1,  2,  3, -1, -1, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1,  2,  2,  3,  3, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 4, -1,  5, -1, -1,  3, -1,  6},
     { 2, -1,  3, -1,  2, -1,  4, -1},
     { 4, -1, -1,  1,  0, -1, -1,  6},
     { 6, -1,  2,  3,  5, -1,  4, -1},
     { 4, -1, -1,  0,  1, -1, -1,  6},
     { 2, -1,  5, -1,  3, -1,  4, -1},
     { 4, -1,  3, -1, -1,  2, -1,  6},
     { 6, -1, -1, -1, -1, -1,  4, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 2,  6,  0,  5,  5,  1,  3,  4},
     { 1, -1, -1,  2, -1, -1,  0, -1},
     { 4, -1, -1,  3,  6, -1, -1,  2},
     {-1, -1, -1,  0, -1, -1, -1, -1},
     {-1, -1, -1,  1,  4, -1, -1, -1},
     {-1, -1, -1,  2, -1, -1, -1, -1},
     {-1, -1, -1,  6,  3, -1, -1, -1},
     {-1, -1, -1,  5, -1, -1, -1, -1},
     {-1, -1, -1,  4,  1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1, -1,  5,  1,  1,  3},
     { 0,  5,  1,  0,  5,  3,  3, -1},
     { 5,  1,  0,  5,  1,  0,  5,  1},
     { 0,  5,  1,  0,  5,  1,  6, -1},
     {-1, -1, -1, -1,  1,  6,  5,  1},
     {-1, -1, -1, -1,  5,  1,  6, -1},
     {-1, -1, -1, -1,  1,  0,  5,  1},
     {-1, -1, -1, -1,  5,  1,  0, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  0,  7,  3, -1, -1,  2,  2},
     {-1,  0,  7,  3, -1, -1,  2, -1},
     {-1,  0,  7,  3, -1, -1,  2,  2},
     {-1,  0,  7,  3, -1,  3,  1, -1},
     {-1,  0,  7,  3, -1,  6,  4,  5},
     {-1,  0,  7,  3, -1,  7,  0, -1},
     {-1,  0,  7,  3, -1,  2,  3,  4},
     {-1,  0,  7,  3, -1,  5,  6, -1},
     {-1, -1, -1, -1, -1,  7,  0,  1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  7,  7,  7,  7, -1},
     { 3,  4,  5, -1, -1, -1,  7, -1},
     { 2, -1, -1, -1, -1, -1, -1,  3},
     { 7, -1, -1, -1, -1, -1,  4, -1},
     { 7, -1, -1, -1,  3,  4,  5,  6},
     { 7, -1, -1,  2,  0,  1,  2, -1},
     { 6, -1, -1, -1,  3,  4,  5,  6},
     { 0,  1, -1, -1, -1, -1, -1, -1},
     { 2,  3,  4, -1, -1, -1, -1, -1},
     { 5,  6,  0, -1, -1, -1, -1, -1}},
    {{-1,  7, -1, -1, -1, -1,  2, -1},
     { 1,  1, -1, -1, -1,  3,  3, -1},
     {-1,  2, -1, -1, -1, -1,  4, -1},
     { 3,  3, -1, -1, -1,  5,  5, -1},
     {-1,  4, -1, -1, -1, -1,  6, -1},
     { 5,  5, -1, -1, -1,  1,  1, -1},
     {-1,  6, -1, -1, -1, -1,  7, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  4, -1, -1, -1, -1,  4, -1},
     { 2, -1, -1,  1, -1, -1,  2, -1},
     { 5, -1, -1,  0,  0, -1, -1,  5},
     { 5, -1, -1,  1, -1, -1,  6, -1},
     {-1,  4,  2,  7,  7,  5,  4, -1},
     {-1, -1, -1,  6, -1, -1, -1, -1},
     {-1, -1, -1,  3,  3, -1, -1, -1},
     {-1, -1, -1,  7, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  1, -1, -1,  2,  3,  4, -1},
     { 2, -1, -1,  3,  0,  4, -1, -1},
     { 4, -1, -1,  2,  3,  1, -1, -1},
     { 3, -1,  4,  3,  0, -1, -1, -1},
     { 4, -1, -1,  2,  5,  1, -1, -1},
     { 3, -1,  4,  5,  0,  4, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 2, -1, -1,  1,  1, -1, -1,  2},
     { 2, -1,  3,  3,  3, -1,  2, -1},
     {-1,  2, -1,  4,  4, -1,  2, -1},
     {-1,  7,  7,  0,  7,  7, -1, -1},
     {-1, -1, -1,  4,  4, -1, -1, -1},
     {-1, -1,  5,  7,  5, -1, -1, -1},
     { 6,  3,  2,  6,  4,  2,  3,  6},
     { 5, -1, -1, -1, -1, -1,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 4,  2,  3,  5,  7,  1,  3,  6},
     { 1, -1, -1,  1, -1, -1,  1, -1},
     { 3,  0,  1,  3,  2,  4,  3,  5},
     { 4, -1, -1,  4, -1, -1,  4, -1},
     {-1,  5, -1, -1,  5, -1, -1,  5},
     { 0,  3,  2,  0,  4,  5,  0, -1},
     {-1,  6, -1, -1,  6, -1, -1,  6},
     { 7, -1, -1,  7, -1, -1,  7, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  5,  4, -1,  1,  1, -1, -1},
     { 5, -1,  4,  1, -1,  1, -1, -1},
     { 0, -1, -1, -1, -1, -1,  0, -1},
     { 0,  6,  4, -1, -1,  4,  2, -1},
     {-1,  4,  3,  5,  2,  6,  3,  6},
     {-1,  2,  6, -1, -1,  5,  4, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  6,  6, -1, -1, -1},
     {-1, -1,  5,  5,  4, -1, -1, -1},
     {-1, -1,  1,  6,  6,  4, -1, -1},
     {-1,  1,  7,  2,  5,  3, -1, -1},
     {-1,  2,  7,  2,  1,  5,  3, -1},
     { 2,  1,  3,  1,  4,  2,  7, -1},
     {-1,  3,  1,  3,  4,  2,  7, -1},
     {-1,  3,  5,  5,  6,  6, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  7,  3, -1, -1, -1, -1},
     {-1,  1,  7,  6, -1, -1, -1, -1},
     {-1,  3,  7,  5,  1,  5, -1, -1},
     { 7,  7,  0,  2,  4,  0,  4, -1},
     { 7,  1,  4,  6,  5,  6,  5,  7},
     { 1,  7,  7,  1,  7,  7,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  1, -1, -1,  1, -1, -1},
     {-1,  5,  6,  1,  5,  6, -1, -1},
     {-1,  1,  1,  2,  2,  1,  1, -1},
     { 4,  7,  1,  0,  1,  7,  4, -1},
     {-1,  3,  7,  5,  7,  5,  3, -1},
     {-1,  1,  1,  1,  1,  1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 4, -1, -1, -1,  5, -1, -1,  4},
     { 6,  6,  7,  6, -1,  4,  5, -1},
     { 4,  2,  7,  5,  2,  2,  6,  4},
     {-1, -1,  4,  1, -1,  5,  2, -1},
     {-1,  5,  2,  7,  7, -1,  7,  4},
     { 4,  6,  5,  4, -1,  4,  2, -1},
     {-1, -1, -1,  4, -1,  4,  1, -1},
     { 0,  0,  0,  5, -1, -1, -1, -1},
     {-1, -1, -1, -1,  0,  0,  0,  0},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1, -1, -1, -1,  0,  0, -1, -1},
     { 2, -1, -1,  0,  1,  0, -1, -1},
     { 3, -1, -1,  0,  2,  2,  0, -1},
     { 4, -1,  0,  1,  1,  1,  0, -1},
     { 5, -1, -1,  0,  4,  4,  0, -1},
     { 6, -1, -1,  4,  4,  4, -1, -1},
     { 7, -1, -1, -1,  4,  4, -1, -1},
     {-1, -1, -1,  0,  1,  0, -1, -1},
     {-1, -1, -1,  0,  1,  1,  0, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  3, -1, -1,  1,  7, -1},
     {-1,  7,  4, -1, -1,  4,  3, -1},
     { 1, -1, -1,  0,  2,  0, -1, -1},
     { 5,  4, -1,  3, -1, -1, -1, -1},
     { 4, -1,  3,  6,  1,  1,  6, -1},
     {-1,  1, -1, -1,  4, -1,  1, -1},
     {-1,  7,  5, -1, -1, -1,  3, -1},
     {-1, -1,  3, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1, -1, -1, -1,  1, -1, -1, -1},
     { 2, -1, -1, -1,  2, -1, -1, -1},
     {-1,  3, -1, -1,  3,  3, -1, -1},
     {-1,  4, -1,  4, -1,  4, -1, -1},
     {-1,  5, -1, -1,  5,  5, -1, -1},
     { 6, -1, -1,  7,  1,  7, -1, -1},
     { 7, -1, -1, -1,  6,  6, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 2, -1, -1,  6, -1,  2,  5,  1},
     { 5, -1,  4, -1,  4, -1,  4, -1},
     { 6, -1, -1,  3, -1, -1, -1,  3},
     { 4,  2,  0, -1, -1, -1,  5, -1},
     {-1, -1, -1,  6, -1,  3,  6, -1},
     {-1, -1,  5, -1,  5, -1, -1, -1},
     {-1, -1, -1,  3, -1,  4,  2,  5},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6, -1, -1, -1,  4, -1, -1,  3},
     { 0,  3, -1, -1,  6, -1,  0, -1},
     {-1, -1,  7, -1,  1, -1,  3, -1},
     { 7, -1,  4,  7, -1,  2, -1, -1},
     { 5,  2,  3,  2,  1,  6, -1,  3},
     {-1, -1,  0,  4,  3,  5,  4, -1},
     {-1,  7,  6, -1, -1,  0, -1, -1},
     { 4,  3, -1, -1, -1,  4,  2, -1},
     { 0, -1, -1, -1, -1, -1,  6, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  1,  2,  5,  1,  6,  3,  0},
     {-1, -1, -1, -1, -1, -1,  4, -1},
     { 0,  5,  2,  7,  1,  6,  2, -1},
     { 3, -1, -1, -1, -1, -1, -1, -1},
     { 6,  7,  6,  4,  0,  5,  2,  6},
     {-1, -1, -1, -1, -1, -1,  1, -1},
     { 6,  1,  4,  0,  6,  2,  3, -1},
     { 0, -1, -1, -1, -1, -1, -1, -1},
     {-1,  0,  4,  5,  3,  7,  6,  0},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  0,  1, -1, -1, -1},
     {-1, -1,  0,  7,  0, -1, -1, -1},
     {-1, -1,  1,  2,  2,  0, -1, -1},
     {-1,  0,  7,  0,  7,  0, -1, -1},
     {-1,  6, -1,  7,  7, -1,  6, -1},
     { 4,  1,  6,  6,  6,  4,  1, -1},
     {-1,  5, -1,  7,  7, -1,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  6, -1, -1, -1},
     {-1, -1,  3,  3,  3, -1, -1, -1},
     {-1, -1,  7,  5,  3,  7, -1, -1},
     {-1,  3, -1,  6, -1,  3, -1, -1},
     { 2, -1, -1,  3,  7, -1, -1,  1},
     { 2,  2, -1,  3, -1,  1,  1, -1},
     {-1,  0,  2,  5,  6,  1,  0, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1,  3,  7, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6, -1, -1, -1, -1,  2, -1},
     {-1,  2,  6,  0,  6,  0, -1, -1},
     {-1,  0, -1, -1, -1, -1, -1, -1},
     { 6, -1, -1, -1, -1, -1, -1, -1},
     {-1,  3,  3,  2,  0,  6,  0,  0},
     {-1,  6, -1, -1, -1, -1,  0, -1},
     {-1, -1, -1,  6,  0,  2,  6, -1},
     {-1,  2,  0, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 0,  7, -1, -1, -1, -1, -1, -1},
     { 1,  5, -1, -1, -1, -1, -1, -1},
     { 7,  2,  5, -1, -1, -1, -1, -1},
     { 6,  3,  4, -1, -1, -1, -1, -1},
     { 5,  5,  4,  4, -1, -1, -1, -1},
     { 3,  3,  5,  3, -1, -1, -1, -1},
     { 1,  2,  2,  5,  3, -1, -1, -1},
     { 1,  0,  0,  7,  6, -1, -1, -1},
     { 3,  3,  5,  5,  7,  6, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  2,  6,  6,  2, -1, -1},
     {-1,  2,  1,  1,  0,  2, -1, -1},
     {-1,  2,  3,  2,  2,  0,  2, -1},
     { 2,  3,  2,  5,  2,  7,  2, -1},
     { 2,  4,  2,  5,  2,  7,  2,  0},
     { 2,  4,  2,  6,  6,  2,  0, -1},
     {-1,  2,  5,  2,  2,  2,  7,  2},
     {-1,  2,  5,  6,  6,  7,  2, -1},
     {-1, -1,  2,  2,  2,  2,  2, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  0, -1, -1,  0, -1, -1},
     { 1,  0,  0,  1,  0,  0,  1, -1},
     { 1,  7,  7,  5,  5,  7,  7,  1},
     { 3,  2, -1,  2, -1,  2,  3, -1},
     { 3,  7, -1,  6,  6, -1,  7,  3},
     { 7, -1, -1,  6, -1, -1,  7, -1},
     { 4,  4,  5, -1, -1,  5,  4,  4},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  6,  3, -1, -1,  3,  6, -1},
     { 6, -1,  2, -1,  2, -1,  6, -1},
     { 2, -1,  0,  1,  1,  0, -1,  2},
     { 5,  0, -1,  7, -1,  0,  5, -1},
     {-1,  5, -1,  6,  6, -1,  5, -1},
     { 7,  1,  4, -1,  4,  1,  7, -1},
     { 7, -1,  4, -1, -1,  4, -1,  7},
     { 2,  0, -1, -1, -1,  0,  2, -1},
     {-1,  2, -1, -1, -1, -1,  2, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 6,  1, -1, -1, -1, -1,  4,  0},
     { 2,  7,  5,  5,  5,  7,  3, -1},
     { 6,  1, -1, -1, -1, -1,  4,  0},
     { 2,  5,  7,  7,  7,  5,  3, -1},
     { 6,  1, -1, -1, -1, -1,  4,  0},
     { 2,  0,  6,  6,  6,  0,  3, -1},
     { 6,  1, -1, -1, -1, -1,  4,  0},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 5, -1, -1,  1,  1, -1, -1,  5},
     { 5, -1,  4, -1,  4, -1,  5, -1},
     {-1,  2,  4, -1, -1,  4,  2, -1},
     { 7,  2, -1, -1, -1,  2,  7, -1},
     { 0, -1,  0,  4,  4,  0, -1,  0},
     { 7,  2, -1, -1, -1,  2,  7, -1},
     {-1,  2,  3, -1, -1,  3,  2, -1},
     { 5, -1,  3, -1,  3, -1,  5, -1},
     { 5, -1, -1,  6,  6, -1, -1,  5},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 2,  2, -1, -1, -1, -1,  5,  5},
     { 5, -1, -1, -1, -1, -1,  2, -1},
     { 5, -1, -1, -1, -1, -1, -1,  2},
     { 1, -1,  1,  5,  1, -1,  3, -1},
     { 5,  2,  5,  3,  1,  2,  5,  2},
     { 2,  0,  5, -1,  2,  0,  5, -1},
     {-1,  3,  7, -1, -1,  3,  7, -1},
     {-1, -1,  2,  0,  5, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 0,  6,  5,  2,  3,  4,  1,  7},
     {-1, -1, -1, -1,  1, -1, -1, -1},
     {-1, -1, -1,  1,  1, -1, -1, -1},
     {-1, -1,  1, -1, -1, -1, -1, -1},
     { 7,  1,  4,  3,  2,  5,  6,  0},
     {-1, -1, -1, -1,  1, -1, -1, -1},
     {-1, -1, -1,  1,  1, -1, -1, -1},
     {-1, -1,  1, -1, -1, -1, -1, -1},
     { 0,  6,  5,  2,  3,  4,  1,  7},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1,  1, -1, -1,  1, -1, -1},
     {-1,  2,  4, -1,  2,  4, -1, -1},
     {-1,  2,  3,  6,  5,  3,  2, -1},
     {-1,  6,  5, -1,  6,  5, -1, -1},
     {-1, -1, -1,  7,  7, -1, -1, -1},
     {-1, -1, -1,  7, -1, -1, -1, -1},
     { 1, -1, -1,  7,  7, -1, -1,  3},
     { 2, -1, -1,  7, -1, -1,  2, -1},
     {-1,  3,  4,  5,  6,  4,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1, -1, -1,  2,  2, -1, -1,  2},
     { 1,  3,  7,  3,  7,  4,  2, -1},
     {-1,  1,  6, -1, -1,  6,  2, -1},
     { 6, -1,  7,  3,  7, -1,  6, -1},
     {-1,  4,  2, -1, -1,  1,  3, -1},
     {-1, -1,  2,  6,  1, -1, -1, -1},
     {-1,  4,  3,  3,  4,  4,  3, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1, -1, -1,  5,  6, -1, -1, -1},
     {-1, -1, -1,  3, -1, -1, -1, -1},
     {-1, -1, -1,  1,  2, -1, -1, -1},
     {-1, -1, -1,  4, -1, -1, -1, -1},
     {-1, -1, -1,  5,  7, -1, -1, -1},
     {-1, -1, -1,  2, -1, -1, -1, -1},
     { 6,  5,  4,  3,  2,  1,  7,  5},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{-1,  0, -1,  1, -1,  2, -1, -1},
     {-1,  4, -1,  5, -1,  6, -1, -1},
     {-1,  7, -1,  0, -1,  2, -1, -1},
     {-1,  6, -1,  3, -1,  6, -1, -1},
     {-1,  1, -1,  1, -1,  2, -1, -1},
     {-1,  3, -1,  5, -1,  0, -1, -1},
     {-1,  2, -1,  4, -1,  6, -1, -1},
     {-1,  3, -1,  6, -1,  7, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1,  1,  2,  2,  3,  3,  4,  4},
     { 5,  5,  6,  7,  6,  5,  5, -1},
     { 6,  4,  3,  3,  2,  2,  1,  6},
     { 4,  6,  5,  7,  6,  3,  1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 7,  4, -1,  1,  2, -1,  4,  7},
     { 5,  5, -1,  2, -1,  4,  4, -1},
     {-1,  5, -1,  7,  7, -1,  4, -1},
     { 1,  0,  6,  7,  6,  0,  2, -1},
     {-1,  2, -1,  5,  3, -1,  1, -1},
     { 1,  1, -1, -1, -1,  2,  2, -1},
     { 6,  1,  4, -1, -1,  4,  2,  6},
     { 5,  3, -1, -1, -1,  3,  5, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 1,  5,  1,  0,  0,  1,  5,  1},
     { 1,  2,  5, -1,  5,  2,  1, -1},
     { 3,  6,  1,  2,  2,  1,  6,  3},
     { 4,  3,  4, -1,  4,  3,  4, -1},
     { 3,  4,  6,  5,  5,  6,  4,  3},
     { 0,  2,  3, -1,  3,  2,  0, -1},
     { 2,  3,  1,  5,  5,  1,  3,  2},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}},
    {{ 3,  0,  2,  7,  5,  7,  6,  5},
     { 6, -1,  1, -1,  2, -1,  1, -1},
     {-1,  6,  4,  0,  3,  4,  5, -1},
     {-1,  5, -1,  1, -1,  4, -1, -1},
     {-1,  7,  3,  5,  6,  5,  3, -1},
     { 1, -1,  2, -1,  4, -1,  2, -1},
     { 6,  4,  4,  6,  6,  5,  5,  1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1},
     {-1, -1, -1, -1, -1, -1, -1, -1}}
};

/* the tile struct
 * type is the bubble number 0-7
 * fallx is the x axis movement for the falling bubble
 * fallvel is the initial upward velocity for the falling bubble
 * ingroup denotes a bubble that is part of a group to be removed
 * anchored denotes a bubble that is anchored to the ceiling
 */
struct tile {
    int type;
    int fallx;
    int fallvel;
    bool ingroup;
    bool anchored;
    bool delete;
};

/* the game context struct
 * score is the current score
 * level is the current level
 * angle is the current cannon direction
 * shots is the number of shots fired since last compression
 * compress is the height of the compressor
 * onboardcnt is the number of unique bubbles on the playing board
 * onboard is the unique bubbles on the playing board
 * nextinq is the pointer to the next bubble in the firing queue
 * queue is the circular buffer of bubbles to be fired
 * elapsedlvl is level elapsed time in 1/100s of seconds
 * elapsedshot is the shot elapsed time in 1/100s of seconds
 * startedshot is when the current shot began
 * playboard is the game playing board
 */
struct game_context {
    unsigned int score;
    unsigned int level;
    int angle;
    int shots;
    int compress;
    int onboardcnt;
    int onboard[NUM_BUBBLES];
    int nextinq;
    int queue[NUM_QUEUE];
    long elapsedlvl;
    long elapsedshot;
    long startedshot;
    struct tile playboard[BB_HEIGHT][BB_WIDTH];
};

static struct highscore highscores[NUM_SCORES];

/* used to denote available resume info */
static bool resume = false;
static bool resume_file = false;
static unsigned int highlevel = 0; /* the highest level beaten */
static unsigned int last_highlevel = 0;

static void bubbles_init(struct game_context* bb);
static bool bubbles_nextlevel(struct game_context* bb);
static void bubbles_getonboard(struct game_context* bb);
static void bubbles_drawboard(struct game_context* bb);
static int  bubbles_fire(struct game_context* bb);
static bool bubbles_collision(struct game_context* bb, int y, int x,
                              int nearrow, int nearcol);
static bool bubbles_ingroup(struct game_context* bb, int row, int col);
static int  bubbles_searchgroup(struct game_context* bb, int row, int col);
static int  bubbles_remove(struct game_context* bb);
static void bubbles_anchored(struct game_context* bb, int row, int col);
static int  bubbles_fall(struct game_context* bb);
static int  bubbles_checklevel(struct game_context* bb);
static void bubbles_recordscore(struct game_context* bb);
static bool bubbles_loadgame(struct game_context* bb);
static void bubbles_savegame(struct game_context* bb);
static inline void bubbles_setcolors(void);
static void bubbles_callback(void* param);
static int  bubbles_handlebuttons(struct game_context* bb, bool animblock,
                                  int timeout);
static int  bubbles(struct game_context* bb);

/*****************************************************************************
* bubbles_init() initializes bubbles data structures.
******************************************************************************/
static void bubbles_init(struct game_context* bb) {
    bubbles_setcolors();
    /* seed the rand generator */
    rb->srand(*rb->current_tick);

    /* check for resumed game */
    if (resume)
    {
        resume = false;
        return;
    }

    bb->score = 0;
    bubbles_nextlevel(bb);
}

/*****************************************************************************
* bubbles_nextlevel() sets up the game for the next level, returns false if
*     there are no more levels.
******************************************************************************/
static bool bubbles_nextlevel(struct game_context* bb) {
    int i, j, pos;

    bb->level++;

    /* check if there are no more levels */
    if(bb->level > NUM_LEVELS) return false;

    /* save highest level */
    if (bb->level-1 > highlevel)
        highlevel = bb->level-1;

    /* set up the play board */
    rb->memset(bb->playboard, 0, sizeof(bb->playboard));
    for(i=0; i<BB_LEVEL_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            pos = (int)level[bb->level-1][i][j];
            if(pos >=0 && pos < NUM_BUBBLES) {
                bb->playboard[i][j].type = pos;
            } else {
                bb->playboard[i][j].type = -1;
            }
        }
    }
    for(i=BB_LEVEL_HEIGHT; i<BB_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            bb->playboard[i][j].type = -1;
        }
    }

    /* fill first bubbles in shot queue */
    bubbles_getonboard(bb);
    for(i=0; i<NUM_QUEUE; i++) {
        bb->queue[i] = bb->onboard[rb->rand()%bb->onboardcnt];
    }

    bb->angle = 0;
    bb->shots = 0;
    bb->compress = 0;
    bb->nextinq = 0;
    bb->elapsedlvl = 0;
    bb->elapsedshot = 0;

    return true;
}

/*****************************************************************************
* bubbles_getonboard() determines which bubble types are on the play board.
******************************************************************************/
static void bubbles_getonboard(struct game_context* bb) {
    int i, j, k;
    bool found;

    bb->onboardcnt = 0;
    rb->memset(bb->onboard, -1, sizeof(bb->onboard));

    for(i=0; i<BB_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[i][j].type >= 0) {
                found = false;

                for(k=0; k<bb->onboardcnt; k++) {
                    if(bb->playboard[i][j].type == bb->onboard[k]) {
                        found = true;
                        break;
                    }
                }

                if(!found) {
                    bb->onboard[bb->onboardcnt] = bb->playboard[i][j].type;
                    bb->onboardcnt++;
                }

                if(bb->onboardcnt == NUM_BUBBLES) return;
            }
        }
    }
}

/*****************************************************************************
* bubbles_drawboard() draws the game board to the buffer but does not update
*     the lcd.
******************************************************************************/
static void bubbles_drawboard(struct game_context* bb) {
    int i, j;
    int w1, w2, h;
    int colmax, indent;
    int tipx, tipy;
    bool evenline = false;
    char *level = "Level";
    char *score = "Score";
    char *next = "Next";
    char *hurry = "HURRY!";
    char str[11];

    /* clear screen */
    rb->lcd_clear_display();
#if ((LCD_HEIGHT >= 128) && (LCD_WIDTH <= 320)) || ((LCD_WIDTH == 132) && (LCD_HEIGHT == 80))
    h = rb->font_get(FONT_SYSFIXED)->height + 1;
#elif (LCD_WIDTH == 640) && (LCD_HEIGHT == 480)
    h = rb->font_get(FONT_SYSFIXED)->height + 6;
#else
    int font = rb->screens[SCREEN_MAIN]->getuifont();
    h = rb->font_get(font)->height + 1;
#endif
    /* draw background */
#ifdef HAVE_LCD_COLOR
    rb->lcd_bitmap(bubbles_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
#endif

    /* display play board */
    for(i=0; i<BB_HEIGHT; i++) {
        colmax = BB_WIDTH;
        if(evenline) {
            colmax--;
            indent = ROW_INDENT;
        } else {
            indent = 0;
        }
        evenline = !evenline;

        for(j=0; j<colmax; j++) {
            if(bb->playboard[i][j].type >= 0 && !bb->playboard[i][j].delete) {
                rb->lcd_bitmap_part(bubbles_emblem,
                  0, EMBLEM_HEIGHT*bb->playboard[i][j].type,
                  STRIDE(   SCREEN_MAIN,
                            BMPWIDTH_bubbles_emblem, BMPHEIGHT_bubbles_emblem),
                  XOFS+indent+BUBBLE_WIDTH*j+(BUBBLE_WIDTH-EMBLEM_WIDTH)/2,
                  YOFS+ROW_HEIGHT*i+(BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2+bb->compress*ROW_HEIGHT,
                  EMBLEM_WIDTH, EMBLEM_HEIGHT);
                rb->lcd_set_drawmode(DRMODE_FG);
                rb->lcd_mono_bitmap((const unsigned char *)bubbles_bubble,
                                    XOFS+indent+BUBBLE_WIDTH*j,
                                    YOFS+ROW_HEIGHT*i+bb->compress*ROW_HEIGHT,
                                    BUBBLE_WIDTH, BUBBLE_HEIGHT);
                rb->lcd_set_drawmode(DRMODE_SOLID);
            }
        }
    }

    /* display bubble to be shot */
    rb->lcd_bitmap_part(bubbles_emblem,
               0, EMBLEM_HEIGHT*bb->queue[bb->nextinq],
               STRIDE(  SCREEN_MAIN,
                        BMPWIDTH_bubbles_emblem, BMPHEIGHT_bubbles_emblem),
               SHOTX+(BUBBLE_WIDTH-EMBLEM_WIDTH)/2,
               SHOTY+(BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2,
               EMBLEM_WIDTH, EMBLEM_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_FG);
    rb->lcd_mono_bitmap((const unsigned char *)bubbles_bubble,
                        SHOTX, SHOTY,
                        BUBBLE_WIDTH, BUBBLE_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_SOLID);

    /* display next bubble to be shot */
#ifndef NEXT_BB_X
    rb->lcd_bitmap_part(bubbles_emblem,
               0, EMBLEM_HEIGHT*bb->queue[(bb->nextinq+1)%NUM_QUEUE],
               STRIDE(  SCREEN_MAIN,
                        BMPWIDTH_bubbles_emblem, BMPHEIGHT_bubbles_emblem),
               XOFS/2-BUBBLE_WIDTH/2+(BUBBLE_WIDTH-EMBLEM_WIDTH)/2,
               SHOTY+(BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2,
               EMBLEM_WIDTH, EMBLEM_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_FG);
    rb->lcd_mono_bitmap((const unsigned char *)bubbles_bubble,
                        XOFS/2-BUBBLE_WIDTH/2, SHOTY,
                        BUBBLE_WIDTH, BUBBLE_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_SOLID);
#else
    rb->lcd_bitmap_part(bubbles_emblem,
               0, EMBLEM_HEIGHT*bb->queue[(bb->nextinq+1)%NUM_QUEUE],
               STRIDE(  SCREEN_MAIN,
                        BMPWIDTH_bubbles_emblem, BMPHEIGHT_bubbles_emblem),
               NEXT_BB_X + NEXT_BB_WIDTH/2-BUBBLE_WIDTH/2+(BUBBLE_WIDTH-EMBLEM_WIDTH)/2,
               NEXT_BB_Y + (BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2 + h,
               EMBLEM_WIDTH, EMBLEM_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_FG);
    rb->lcd_mono_bitmap((const unsigned char *)bubbles_bubble,
                        NEXT_BB_X + NEXT_BB_WIDTH/2-BUBBLE_WIDTH/2, NEXT_BB_Y + h,
                        BUBBLE_WIDTH, BUBBLE_HEIGHT);
    rb->lcd_set_drawmode(DRMODE_SOLID);
#endif

    /* draw bounding lines */
#ifndef HAVE_LCD_COLOR
    rb->lcd_vline(XOFS-1, 0, LCD_HEIGHT);
    rb->lcd_vline(XOFS+BUBBLE_WIDTH*BB_WIDTH, 0, LCD_HEIGHT);
#endif
    rb->lcd_hline(XOFS, XOFS+BUBBLE_WIDTH*BB_WIDTH-1, YOFS+bb->compress*ROW_HEIGHT-1);
    rb->lcd_hline(XOFS, XOFS+BUBBLE_WIDTH*BB_WIDTH-1,
                  YOFS+ROW_HEIGHT*(BB_HEIGHT-2)+BUBBLE_HEIGHT);

    /* draw arrow */
    tipx = SHOTX+BUBBLE_WIDTH/2+(((fp14_sin(bb->angle)>>4)*BUBBLE_WIDTH*3/2)>>10);
    tipy = SHOTY+BUBBLE_HEIGHT/2-(((fp14_cos(bb->angle)>>4)*BUBBLE_HEIGHT*3/2)>>10);

    rb->lcd_drawline(SHOTX+BUBBLE_WIDTH/2+(((fp14_sin(bb->angle)>>4)*BUBBLE_WIDTH/2)>>10),
                     SHOTY+BUBBLE_HEIGHT/2-(((fp14_cos(bb->angle)>>4)*BUBBLE_HEIGHT/2)>>10),
                     tipx, tipy);
    xlcd_filltriangle(tipx, tipy,
                      tipx+(((fp14_sin(bb->angle-135)>>4)*BUBBLE_WIDTH/3)>>10),
                      tipy-(((fp14_cos(bb->angle-135)>>4)*BUBBLE_HEIGHT/3)>>10),
                      tipx+(((fp14_sin(bb->angle+135)>>4)*BUBBLE_WIDTH/3)>>10),
                      tipy-(((fp14_cos(bb->angle+135)>>4)*BUBBLE_HEIGHT/3)>>10));

    /* draw text */
    rb->snprintf(str, 4, "%d", bb->level);
    rb->lcd_getstringsize(level, &w1, NULL);
    rb->lcd_getstringsize(str, &w2, NULL);
#ifndef LEVEL_TXT_X
    rb->lcd_putsxy(XOFS/2-w1/2, 2, level);
    rb->lcd_putsxy(XOFS/2-w2/2, 2+h, str);
#else
    rb->lcd_putsxy(LEVEL_TXT_X+(LEVEL_TXT_WIDTH/2-w1/2), LEVEL_TXT_Y, level);
    rb->lcd_putsxy(LEVEL_TXT_X+(LEVEL_TXT_WIDTH/2-w2/2), LEVEL_TXT_Y+h, str);
#endif

    rb->snprintf(str, 10, "%d", bb->score);
    rb->lcd_getstringsize(score, &w1,NULL);
    rb->lcd_getstringsize(str, &w2, NULL);
#ifndef SCORE_TXT_X
    rb->lcd_putsxy(XOFS/2-w1/2, 29, score);
    rb->lcd_putsxy(XOFS/2-w2/2, 29+h, str);
#else
    rb->lcd_putsxy(SCORE_TXT_X+(SCORE_TXT_WIDTH/2-w1/2), SCORE_TXT_Y, score);
    rb->lcd_putsxy(SCORE_TXT_X+(SCORE_TXT_WIDTH/2-w2/2), SCORE_TXT_Y+h, str);
#endif

    rb->lcd_getstringsize(next, &w1, NULL);
#ifndef NEXT_BB_X
    rb->lcd_putsxy(XOFS/2-w1/2, SHOTY-h, next);
#else
    rb->lcd_putsxy(NEXT_BB_X+(NEXT_BB_WIDTH/2-w1/2), NEXT_BB_Y, next);
#endif


    if(bb->elapsedshot >= (MAX_SHOTTIME*7)/10) {
        rb->lcd_getstringsize(hurry, &w1, &h);
        /* screen is too small for the message to be centered (Clip Zip) */
        #if (LCD_WIDTH <= 96)
            rb->lcd_putsxy(LCD_WIDTH/2-w1/2+LCD_WIDTH/6, LCD_HEIGHT/2-h/2, hurry);
        #else
            rb->lcd_putsxy(LCD_WIDTH/2-w1/2, LCD_HEIGHT/2-h/2, hurry);
        #endif
    }
}

/*****************************************************************************
* bubbles_fire() fires the current bubble, reloads the cannon, attaches
*     bubble to playboard, removes appropriate bubbles, and advances the
*     the compressor.
******************************************************************************/
static int bubbles_fire(struct game_context* bb) {
    int bubblecur;
    long shotxinc, shotyinc;
    long shotxofs, shotyofs;
    int shotxdirec = 1;
    long tempxofs, tempyofs;
    int nearrow, nearcol;
    int lastrow = BB_HEIGHT-1;
    int lastcol = (BB_WIDTH-1)/2;
    int buttonres;
    long lasttick, currenttick;

    /* get current bubble */
    bubblecur = bb->queue[bb->nextinq];
    shotxinc = ((fp14_sin(bb->angle)>>4)*BUBBLE_WIDTH)/3;
    shotyinc = ((-1*(fp14_cos(bb->angle)>>4))*BUBBLE_HEIGHT)/3;
    shotxofs = shotyofs = 0;

    /* advance the queue */
    bb->queue[bb->nextinq] = bb->onboard[rb->rand()%bb->onboardcnt];
    bb->nextinq = (bb->nextinq+1)%NUM_QUEUE;
    bubbles_drawboard(bb);
    rb->lcd_update_rect(0, 0, XOFS, LCD_HEIGHT);

    /* move the bubble across the play board */
    lasttick = *rb->current_tick;

    while(true) {
        /* move the bubble one step */
        shotyofs += shotyinc;
        shotxofs += shotxinc*shotxdirec;

        /* check for bounce off sides */
        if(SHOTX+(shotxofs>>10) < XOFS) {
            shotxofs += 2*((XOFS<<10)-(((SHOTX)<<10)+shotxofs));
            shotxdirec *= -1;
        } else if(SHOTX+(shotxofs>>10) > XOFS+(BB_WIDTH-1)*BUBBLE_WIDTH) {
            shotxofs -= 2*((((SHOTX)<<10)+shotxofs)-
                        ((XOFS<<10)+(((BB_WIDTH-1)*BUBBLE_WIDTH)<<10)));
            shotxdirec *= -1;
        }

        tempxofs = shotxofs>>10;
        tempyofs = shotyofs>>10;

        /* display shot */
        bubbles_drawboard(bb);
        rb->lcd_bitmap_part(bubbles_emblem, 0, EMBLEM_HEIGHT*bubblecur,
                       STRIDE(  SCREEN_MAIN,
                                BMPWIDTH_bubbles_emblem,
                                BMPHEIGHT_bubbles_emblem),
                       SHOTX+tempxofs+(BUBBLE_WIDTH-EMBLEM_WIDTH)/2,
                       SHOTY+tempyofs+(BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2,
                       EMBLEM_WIDTH, EMBLEM_HEIGHT);
        rb->lcd_set_drawmode(DRMODE_FG);
        rb->lcd_mono_bitmap((const unsigned char *)bubbles_bubble,
                            SHOTX+tempxofs, SHOTY+tempyofs,
                            BUBBLE_WIDTH, BUBBLE_HEIGHT);
        rb->lcd_set_drawmode(DRMODE_SOLID);
        rb->lcd_update_rect(XOFS, 0, BB_WIDTH*BUBBLE_WIDTH, LCD_HEIGHT);

        /* find nearest position */
        nearrow = ((SHOTY+tempyofs)-
                      (YOFS+bb->compress*ROW_HEIGHT)+
                      (ROW_HEIGHT/2))/ROW_HEIGHT;
        if(nearrow >= BB_HEIGHT) nearrow = BB_HEIGHT-1;

        if(nearrow%2) { /* odd row */
            nearcol = ((SHOTX+tempxofs)-
                          (XOFS+ROW_INDENT)+
                          (BUBBLE_WIDTH/2))/BUBBLE_WIDTH;
            if(nearcol >= BB_WIDTH-1) nearcol = BB_WIDTH-2;
        } else {        /* even row */
            nearcol = ((SHOTX+tempxofs)-XOFS+(BUBBLE_WIDTH/2))/BUBBLE_WIDTH;
            if(nearcol >= BB_WIDTH) nearcol = BB_WIDTH-1;
        }
        if(nearcol < 0) nearcol = 0;

        /* if nearest position is occupied attach to last position */
        if(bb->playboard[nearrow][nearcol].type >= 0) {
            bb->playboard[lastrow][lastcol].type = bubblecur;
            break;
        }

        /* save last position */
        lastrow = nearrow;
        lastcol = nearcol;

        /* if collision with neighbor then attach shot */
        if(bubbles_collision(bb, YOFS+SHOTY+tempyofs, SHOTX+tempxofs,
                             nearrow, nearcol)) {
            bb->playboard[nearrow][nearcol].type = bubblecur;
            break;
        }

        /* if at top then attach shot to the ceiling */
        if(nearrow == 0 && SHOTY+tempyofs <= YOFS+bb->compress*ROW_HEIGHT) {
            bb->playboard[nearrow][nearcol].type = bubblecur;
            break;
        }

        /* handle button events */
        buttonres = bubbles_handlebuttons(bb, true, 0);
        if(buttonres != BB_NONE) return buttonres;

        /* framerate limiting */
        currenttick = *rb->current_tick;
        if(currenttick-lasttick < HZ/MAX_FPS) {
            rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
        } else {
            rb->yield();
        }
        lasttick = currenttick;
    }

    bubbles_drawboard(bb);
    rb->lcd_update();

    /* clear appropriate bubbles from playing board */
    if(bubbles_ingroup(bb, lastrow, lastcol)) {
        buttonres = bubbles_remove(bb);
        if(buttonres != BB_NONE) return buttonres;
    }

    /* update shots and compress amount */
    bb->shots++;
    if(bb->shots >= NUM_COMPRESS) {
        bb->shots = 0;
        bb->compress++;
    }

    return BB_NONE;
}

/*****************************************************************************
* bubbles_collision() determines if a fired bubble has collided with another
*     bubble.
******************************************************************************/
static bool bubbles_collision(struct game_context* bb, int y, int x,
                              int nearrow, int nearcol) {
    int nx, ny;
    int adj = nearrow%2;

    /* check neighbors */
    if(nearcol-1 >= 0) {
        if(bb->playboard[nearrow][nearcol-1].type >= 0) {
            nx = XOFS+(nearrow%2 ? ROW_INDENT : 0)+BUBBLE_WIDTH*(nearcol-1);
            ny = YOFS+ROW_HEIGHT*nearrow+bb->compress*ROW_HEIGHT;
            if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
        }
    }

    if(nearcol-1+adj >= 0) {
        if(nearrow-1 >= 0) {
            if(bb->playboard[nearrow-1][nearcol-1+adj].type >= 0) {
                nx = XOFS+((nearrow-1)%2 ? ROW_INDENT : 0)+
                     BUBBLE_WIDTH*(nearcol-1+adj);
                ny = YOFS+ROW_HEIGHT*(nearrow-1)+bb->compress*ROW_HEIGHT;
                if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
            }
        }

        if(nearrow+1 < BB_HEIGHT) {
            if(bb->playboard[nearrow+1][nearcol-1+adj].type >= 0) {
                nx = XOFS+((nearrow+1)%2 ? ROW_INDENT : 0)+
                     BUBBLE_WIDTH*(nearcol-1+adj);
                ny = YOFS+ROW_HEIGHT*(nearrow+1)+bb->compress*ROW_HEIGHT;
                if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
            }
        }
    }

    if(nearcol+adj >= 0) {
        if(nearrow-1 >= 0) {
            if(bb->playboard[nearrow-1][nearcol+adj].type >= 0) {
                nx = XOFS+((nearrow-1)%2 ? ROW_INDENT : 0)+
                     BUBBLE_WIDTH*(nearcol+adj);
                ny = YOFS+ROW_HEIGHT*(nearrow-1)+bb->compress*ROW_HEIGHT;
                if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
            }
        }

        if(nearrow+1 < BB_HEIGHT) {
            if(bb->playboard[nearrow+1][nearcol+adj].type >= 0) {
                nx = XOFS+((nearrow+1)%2 ? ROW_INDENT : 0)+
                     BUBBLE_WIDTH*(nearcol+adj);
                ny = YOFS+ROW_HEIGHT*(nearrow+1)+bb->compress*ROW_HEIGHT;
                if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
            }
        }
    }

    if(nearcol+1 < BB_WIDTH-adj) {
        if(bb->playboard[nearrow][nearcol+1].type >= 0) {
            nx = XOFS+(nearrow%2 ? ROW_INDENT : 0)+BUBBLE_WIDTH*(nearcol+1);
            ny = YOFS+ROW_HEIGHT*nearrow+bb->compress*ROW_HEIGHT;
            if((x-nx)*(x-nx)+(y-ny)*(y-ny) < MIN_DISTANCE) return true;
        }
    }

    return false;
}

/*****************************************************************************
* bubbles_ingroup() marks all bubbles that form the current group.
******************************************************************************/
static bool bubbles_ingroup(struct game_context* bb, int row, int col) {
    int i, j;
    int count;

    count = bubbles_searchgroup(bb, row, col);

    /* unmark group if too small */
    if(count < 3) {
        for(i=0; i<BB_HEIGHT; i++) {
            for(j=0; j<BB_WIDTH; j++) {
                bb->playboard[i][j].ingroup = false;
            }
        }

        return false;
    }

    return true;
}

/*****************************************************************************
* bubbles_searchgroup() return the size of the group of bubbles of the same
*     type that the current bubble belongs to.
******************************************************************************/
static int bubbles_searchgroup(struct game_context* bb, int row, int col) {
    int i, adj;
    int myrow, mycol, mytype;
    int count = 0;

    struct coord {
        int row;
        int col;
    } search[(2*BB_WIDTH-1)*(BB_HEIGHT/2)];

    /* search initial bubble */
    bb->playboard[row][col].ingroup = true;
    search[count].row = row;
    search[count].col = col;
    count++;

    /* breadth-first search neighbors */
    for(i=0; i<count; i++) {
        myrow = search[i].row;
        mycol = search[i].col;
        mytype = bb->playboard[myrow][mycol].type;
        adj = myrow%2;

        if(mycol-1 >= 0) {
            if(bb->playboard[myrow][mycol-1].type == mytype &&
               !bb->playboard[myrow][mycol-1].ingroup) {
                bb->playboard[myrow][mycol-1].ingroup = true;
                search[count].row = myrow;
                search[count].col = mycol-1;
                count++;
            }
        }

        if(mycol-1+adj >= 0) {
            if(myrow-1 >= 0) {
                if(bb->playboard[myrow-1][mycol-1+adj].type == mytype &&
                   !bb->playboard[myrow-1][mycol-1+adj].ingroup) {
                    bb->playboard[myrow-1][mycol-1+adj].ingroup = true;
                    search[count].row = myrow-1;
                    search[count].col = mycol-1+adj;
                    count++;
                }
            }

            if(myrow+1 < BB_HEIGHT) {
                if(bb->playboard[myrow+1][mycol-1+adj].type == mytype &&
                   !bb->playboard[myrow+1][mycol-1+adj].ingroup) {
                    bb->playboard[myrow+1][mycol-1+adj].ingroup = true;
                    search[count].row = myrow+1;
                    search[count].col = mycol-1+adj;
                    count++;
                }
            }
        }

        if(mycol+adj >= 0) {
            if(myrow-1 >= 0) {
                if(bb->playboard[myrow-1][mycol+adj].type == mytype &&
                   !bb->playboard[myrow-1][mycol+adj].ingroup) {
                    bb->playboard[myrow-1][mycol+adj].ingroup = true;
                    search[count].row = myrow-1;
                    search[count].col = mycol+adj;
                    count++;
                }
            }

            if(myrow+1 < BB_HEIGHT) {
                if(bb->playboard[myrow+1][mycol+adj].type == mytype &&
                   !bb->playboard[myrow+1][mycol+adj].ingroup) {
                    bb->playboard[myrow+1][mycol+adj].ingroup = true;
                    search[count].row = myrow+1;
                    search[count].col = mycol+adj;
                    count++;
                }
            }
        }

        if(mycol+1 < BB_WIDTH-adj) {
            if(bb->playboard[myrow][mycol+1].type == mytype &&
               !bb->playboard[myrow][mycol+1].ingroup) {
                bb->playboard[myrow][mycol+1].ingroup = true;
                search[count].row = myrow;
                search[count].col = mycol+1;
                count++;
            }
        }
    }

    return count;
}

/*****************************************************************************
* bubbles_remove() removes all bubbles in the current group and all
*     unanchored bubbles from the play board.
******************************************************************************/
static int bubbles_remove(struct game_context* bb) {
    int i, j;
    int buttonres;

    /* determine all anchored bubbles */
    for(j=0; j<BB_WIDTH; j++) {
        if(bb->playboard[0][j].type >= 0 && !bb->playboard[0][j].ingroup) {
            bubbles_anchored(bb, 0, j);
        }
    }

    /* mark bubbles to be deleted */
    for(i=0; i<BB_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[i][j].type >= 0 &&
              (!bb->playboard[i][j].anchored || bb->playboard[i][j].ingroup)) {
                bb->playboard[i][j].delete = true;
            }
        }
    }

    /* animate falling bubbles */
    buttonres = bubbles_fall(bb);
    if(buttonres != BB_NONE) return buttonres;

    /* remove bubbles */
    for(i=0; i<BB_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[i][j].delete) {
                bb->playboard[i][j].ingroup = false;
                bb->playboard[i][j].type = -1;
                bb->playboard[i][j].delete = false;
            } else {
                bb->playboard[i][j].anchored = false;
            }
        }
    }

    bubbles_getonboard(bb);

    return BB_NONE;
}

/*****************************************************************************
* bubbles_anchored() marks all bubbles that are anchored in some way to the
*     current bubble.
******************************************************************************/
static void bubbles_anchored(struct game_context* bb, int row, int col) {
    int i, adj;
    int myrow, mycol;
    int count = 0;

    struct coord {
        int row;
        int col;
    } search[(2*BB_WIDTH-1)*(BB_HEIGHT/2)];

    /* search initial bubble */
    bb->playboard[row][col].anchored = true;
    search[count].row = row;
    search[count].col = col;
    count++;

    /* breadth-first search neighbors */
    for(i=0; i<count; i++) {
        myrow = search[i].row;
        mycol = search[i].col;
        adj = myrow%2;

        if(mycol-1 >= 0) {
            if(bb->playboard[myrow][mycol-1].type >= 0 &&
               !bb->playboard[myrow][mycol-1].ingroup &&
               !bb->playboard[myrow][mycol-1].anchored) {
                bb->playboard[myrow][mycol-1].anchored = true;
                search[count].row = myrow;
                search[count].col = mycol-1;
                count++;
            }
        }

        if(mycol-1+adj >= 0) {
            if(myrow-1 >= 0) {
                if(bb->playboard[myrow-1][mycol-1+adj].type >= 0 &&
                   !bb->playboard[myrow-1][mycol-1+adj].ingroup &&
                   !bb->playboard[myrow-1][mycol-1+adj].anchored) {
                    bb->playboard[myrow-1][mycol-1+adj].anchored = true;
                    search[count].row = myrow-1;
                    search[count].col = mycol-1+adj;
                    count++;
                }
            }

            if(myrow+1 < BB_HEIGHT) {
                if(bb->playboard[myrow+1][mycol-1+adj].type >= 0 &&
                   !bb->playboard[myrow+1][mycol-1+adj].ingroup &&
                   !bb->playboard[myrow+1][mycol-1+adj].anchored) {
                    bb->playboard[myrow+1][mycol-1+adj].anchored = true;
                    search[count].row = myrow+1;
                    search[count].col = mycol-1+adj;
                    count++;
                }
            }
        }

        if(mycol+adj >= 0) {
            if(myrow-1 >= 0) {
                if(bb->playboard[myrow-1][mycol+adj].type >= 0 &&
                   !bb->playboard[myrow-1][mycol+adj].ingroup &&
                   !bb->playboard[myrow-1][mycol+adj].anchored) {
                    bb->playboard[myrow-1][mycol+adj].anchored = true;
                    search[count].row = myrow-1;
                    search[count].col = mycol+adj;
                    count++;
                }
            }

            if(myrow+1 < BB_HEIGHT) {
                if(bb->playboard[myrow+1][mycol+adj].type >= 0 &&
                   !bb->playboard[myrow+1][mycol+adj].ingroup &&
                   !bb->playboard[myrow+1][mycol+adj].anchored) {
                    bb->playboard[myrow+1][mycol+adj].anchored = true;
                    search[count].row = myrow+1;
                    search[count].col = mycol+adj;
                    count++;
                }
            }
        }

        if(mycol+1 < BB_WIDTH-adj) {
            if(bb->playboard[myrow][mycol+1].type >= 0 &&
               !bb->playboard[myrow][mycol+1].ingroup &&
               !bb->playboard[myrow][mycol+1].anchored) {
                bb->playboard[myrow][mycol+1].anchored = true;
                search[count].row = myrow;
                search[count].col = mycol+1;
                count++;
            }
        }
    }
}

/*****************************************************************************
* bubbles_fall() makes removed bubbles fall from the screen.
******************************************************************************/
static int bubbles_fall(struct game_context* bb) {
    int i, j;
    int count;
    int indent;
    int xofs, yofs;
    int buttonres;
    bool onscreen;
    long lasttick, currenttick;

    /* give all falling bubbles an x axis movement */
    for(i=0; i<BB_HEIGHT; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[i][j].delete) {
                bb->playboard[i][j].fallx = rb->rand()%25 - 12;
                bb->playboard[i][j].fallvel = rb->rand()%5 + 6;
            }
        }
    }

    /* draw bubbles falling off the screen
     * follows y=x^2-8x scaled to bubble size
     */
    lasttick = *rb->current_tick;

    for(count=1; ;count++) {
        onscreen = false;
        bubbles_drawboard(bb);

        for(i=0; i<BB_HEIGHT; i++) {
            for(j=0; j<BB_WIDTH; j++) {
                if(bb->playboard[i][j].delete) {
                    indent = (i%2 ? ROW_INDENT : 0);
                    xofs = ((bb->playboard[i][j].fallx*count)*BUBBLE_WIDTH)/48;
                    yofs = ((count*count - bb->playboard[i][j].fallvel*count)*
                           BUBBLE_HEIGHT)/20;

                    /* draw bubble if it is still on the screen */
                    if(YOFS+ROW_HEIGHT*i+bb->compress*ROW_HEIGHT+yofs
                       <= LCD_HEIGHT) {
                        onscreen = true;

                        rb->lcd_bitmap_part(bubbles_emblem, 0,
                                EMBLEM_HEIGHT*bb->playboard[i][j].type,
                                STRIDE( SCREEN_MAIN,
                                        BMPWIDTH_bubbles_emblem,
                                        BMPHEIGHT_bubbles_emblem),
                                XOFS+indent+BUBBLE_WIDTH*j+
                                    (BUBBLE_WIDTH-EMBLEM_WIDTH)/2+xofs,
                                YOFS+ROW_HEIGHT*i+(BUBBLE_HEIGHT-EMBLEM_HEIGHT)/2+
                                    bb->compress*ROW_HEIGHT+yofs,
                                EMBLEM_WIDTH, EMBLEM_HEIGHT);
                        rb->lcd_set_drawmode(DRMODE_FG);
                        rb->lcd_mono_bitmap(
                                (const unsigned char *)bubbles_bubble,
                                XOFS+indent+BUBBLE_WIDTH*j+xofs,
                                YOFS+ROW_HEIGHT*i+bb->compress*ROW_HEIGHT+yofs,
                                BUBBLE_WIDTH, BUBBLE_HEIGHT);
                        rb->lcd_set_drawmode(DRMODE_SOLID);
                    }
                }
            }
        }

        rb->lcd_update();

        /* break out if all bubbles are off the screen */
        if(!onscreen) break;

        /* handle button events */
        buttonres = bubbles_handlebuttons(bb, true, 0);
        if(buttonres != BB_NONE) return buttonres;

        /* framerate limiting */
        currenttick = *rb->current_tick;
        if(currenttick-lasttick < HZ/MAX_FPS) {
            rb->sleep((HZ/MAX_FPS)-(currenttick-lasttick));
        } else {
            rb->yield();
        }
        lasttick = currenttick;
    }

    return BB_NONE;
}

/*****************************************************************************
* bubbles_checklevel() checks the state of the playboard for a win or loss.
******************************************************************************/
static int bubbles_checklevel(struct game_context* bb) {
    int i, j;
    int points;
    char str[13];

    bubbles_drawboard(bb);
    rb->lcd_update();

    /* check for bubbles below cut off point */
    for(i=0; i<=bb->compress; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[BB_HEIGHT-1-i][j].type >= 0) return BB_LOSE;
      }
    }

    /* check for bubbles above cut off point */
    for(i=0; i<BB_HEIGHT-1-bb->compress; i++) {
        for(j=0; j<BB_WIDTH; j++) {
            if(bb->playboard[i][j].type >= 0) return BB_NONE;
        }
    }

    /* level complete, record score */
    points = 100 - bb->elapsedlvl/100;
    if(points > 0) {
        bb->score += points;
    } else {
        points = 0;
    }

    rb->snprintf(str, 12, "%d points", points);
    rb->splash(HZ, str);

    /* advance to the next level */
    if(!bubbles_nextlevel(bb)) {
        return BB_WIN;
    }

    bubbles_drawboard(bb);
    rb->lcd_update();
    rb->snprintf(str, 12, "Level %d", bb->level);
    rb->splash(HZ, str);
    bubbles_drawboard(bb);
    rb->lcd_update();

    return BB_NONE;
}

/*****************************************************************************
* bubbles_recordscore() inserts a high score into the high scores list and
*     returns the high score position.
******************************************************************************/
static void bubbles_recordscore(struct game_context* bb) {

    int position;

    position = highscore_update(bb->score, bb->level-1, "",
                                highscores, NUM_SCORES);
    if (position != -1)
    {
        if (position == 0)
            rb->splash(HZ*2, "New High Score");
        highscore_show(position, highscores, NUM_SCORES, true);
    }
}

/*****************************************************************************
* bubbles_loaddata() loads highest level beaten.
******************************************************************************/
static void bubbles_loaddata(void) {
    int fd;

    last_highlevel = highlevel = 0;
    /* open data file */
    fd = rb->open(DATA_FILE, O_RDONLY);
    if (fd < 0) return;

    /* read in saved game */
    if (rb->read(fd, &highlevel, sizeof(highlevel)) < (long)sizeof(highlevel))
    {
        highlevel = 0;
    }
    if (highlevel >= NUM_LEVELS)
        highlevel = NUM_LEVELS-1;
    last_highlevel = highlevel;

    rb->close(fd);
}

/*****************************************************************************
* bubbles_savedata() saves the current game state.
******************************************************************************/
static void bubbles_savedata(void) {
    int fd;

    if (last_highlevel >= highlevel) /* no need to save */
        return;

    fd = rb->open(DATA_FILE, O_WRONLY|O_CREAT, 0666);
    if (fd < 0) return;

    rb->write(fd, &highlevel, sizeof(highlevel));

    rb->close(fd);
}

/*****************************************************************************
* bubbles_loadgame() loads the saved game and returns load success.
******************************************************************************/
static bool bubbles_loadgame(struct game_context* bb) {
    int fd;

    bool ret = true;
    /* open game file */
    fd = rb->open(SAVE_FILE, O_RDONLY);
    if(fd < 0) return false;

    /* read in saved game */
    if(rb->read(fd, bb, sizeof(struct game_context))
            < (long)sizeof(struct game_context))
    {
        ret = false;
    }

    rb->close(fd);

    return ret;
}

/*****************************************************************************
* bubbles_savegame() saves the current game state.
******************************************************************************/
static void bubbles_savegame(struct game_context* bb) {
    int fd;

    if (!resume) /* nothing to save */
        return;
    /* write out the game state to the save file */
    fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
    if (fd < 0)
    {
        rb->splash(HZ/2, "Failed to save game");
        return;
    }
    if (rb->write(fd, bb, sizeof(struct game_context)) <= 0)
    {
        rb->splash(HZ/2, "Failed to save game");
    }

    rb->close(fd);
}

/*****************************************************************************
* bubbles_setcolors() set the foreground and background colors.
******************************************************************************/
static inline void bubbles_setcolors(void) {
#ifdef HAVE_LCD_COLOR
    rb->lcd_set_background(LCD_RGBPACK(181,181,222));
    rb->lcd_set_foreground(LCD_BLACK);
#endif
}

/*****************************************************************************
* bubbles_callback() is the default event handler callback which is called
*     on usb connect and shutdown.
******************************************************************************/
static void bubbles_callback(void* param) {
    (void) param;
    highscore_save(SCORE_FILE, highscores, NUM_SCORES);
}

/*****************************************************************************
* bubbles_handlebuttons() handles button events during a game.
******************************************************************************/
static int bubbles_handlebuttons(struct game_context* bb, bool animblock,
                                 int timeout) {
    int button;
    int buttonres;
    long start;
    const struct button_mapping *plugin_contexts[]
                     = { pla_main_ctx,
#ifdef HAVE_REMOTE_LCD
                         pla_remote_ctx,
#endif
                        };

    if (timeout < 0)
        timeout = 0;
    button = pluginlib_getaction(timeout,plugin_contexts,ARRAYLEN(plugin_contexts));
#if defined(HAS_BUTTON_HOLD) && !defined(HAVE_REMOTE_LCD_AS_MAIN)
    /* FIXME: Should probably check remote hold here */
    if (rb->button_hold())
        button = BUBBLES_PAUSE;
#endif

    switch(button){
        case BUBBLES_LEFT_REP:
            if(bb->angle > MIN_ANGLE) bb->angle -= ANGLE_STEP_REP;
            break;
        case BUBBLES_LEFT:   /* change angle to the left */
            if(bb->angle > MIN_ANGLE) bb->angle -= ANGLE_STEP;
            break;

        case BUBBLES_RIGHT_REP:
            if(bb->angle < MAX_ANGLE) bb->angle += ANGLE_STEP_REP;
            break;
        case BUBBLES_RIGHT:  /* change angle to the right */
            if(bb->angle < MAX_ANGLE) bb->angle += ANGLE_STEP;
            break;

        case BUBBLES_FIRE: /* fire the shot */
        case BUBBLES_FIRE_REPEAT:
            if(!animblock) {
                bb->elapsedlvl += bb->elapsedshot;
                bb->elapsedshot = 0;
                buttonres = bubbles_fire(bb);
                if(buttonres != BB_NONE) return buttonres;
                buttonres = bubbles_checklevel(bb);
                if(buttonres != BB_NONE) return buttonres;
                bb->startedshot = *rb->current_tick;
            }
            break;

        case BUBBLES_PAUSE:  /* pause the game */
            start = *rb->current_tick;
            rb->splash(0, "Paused");

            while(pluginlib_getaction(TIMEOUT_BLOCK,plugin_contexts,
                    ARRAYLEN(plugin_contexts))
                 != BUBBLES_PAUSE);
            bb->startedshot += *rb->current_tick-start;
            bubbles_drawboard(bb);
            rb->lcd_update();
            break;

        case BUBBLES_QUIT1:
        case BUBBLES_QUIT2:   /* end the game */
            if(!animblock) {
                resume = true;
                return BB_END;
            }
            break;

        case ACTION_UNKNOWN:
        case ACTION_NONE:    /* no button pressed */
            break;

        default:
            if(rb->default_event_handler_ex(button, bubbles_callback,
               (void*) bb) == SYS_USB_CONNECTED)
                return BB_USB;
            break;
    }

    return BB_NONE;
}

static int bubbles_menu_cb(int action,
                             const struct menu_item_ex *this_item,
                             struct gui_synclist *this_list)
{
    (void)this_list;
    int i = ((intptr_t)this_item);
    if(action == ACTION_REQUEST_MENUITEM
       && !resume && (i==0))
        return ACTION_EXIT_MENUITEM;
    return action;
}

/*****************************************************************************
* bubbles_menu() is the initial menu at the start of the game.
******************************************************************************/
static int bubbles_menu(struct game_context* bb) {
    static unsigned int startlevel = 0;
    int selected = 0;
    bool startgame = false;

    MENUITEM_STRINGLIST(menu,"Bubbles",bubbles_menu_cb,
                        "Resume Game", "Start New Game",
                        "Level", "High Scores", "Playback Control",
                        "Quit without Saving", "Quit");

    while(!startgame){
        switch (rb->do_menu(&menu, &selected, NULL, false))
        {
            case 0: /* resume game */
                startgame = true;
                if(resume_file)
                    rb->remove(SAVE_FILE);
                resume_file = false;
                break;
            case 1: /* new game */
                bb->level = startlevel;
                startgame = true;
                resume = false;
                resume_file = false;
                break;
            case 2: /* choose level */
                startlevel++;
                rb->set_int("Choose start level", "", UNIT_INT, &startlevel,
                            NULL, 1, 1, highlevel+1, NULL);
                startlevel--;
                break;
            case 3: /* High scores */
                highscore_show(-1, highscores, NUM_SCORES, true);
                break;
            case 4: /* Playback Control */
                playback_control(NULL);
                break;
            case 5: /* quit but don't save */
                return BB_QUIT_WITHOUT_SAVING;
            case 6: /* save and quit  */
                return BB_QUIT;
            case MENU_ATTACHED_USB:
                bubbles_callback(bb);
                return BB_USB;
        }
    }
    return 0;
}

/*****************************************************************************
* bubbles() is the main game subroutine, it returns the final game status.
******************************************************************************/
static int bubbles(struct game_context* bb) {
    int buttonres;
    long timeout;

    /********************
    *       menu        *
    ********************/
    buttonres = bubbles_menu(bb);
    if(buttonres != 0)
        return buttonres;

    /********************
    *       init        *
    ********************/
    bubbles_init(bb);
    bubbles_drawboard(bb);
    rb->lcd_update();

    /**********************
    *        play         *
    **********************/
    bb->startedshot = *rb->current_tick;

    while(true) {
        /* refresh the board */
        bubbles_drawboard(bb);
        rb->lcd_update();

        /* manange idle framerate */
        bb->elapsedshot = *rb->current_tick-bb->startedshot;

        if(MAX_SHOTTIME-bb->elapsedshot < HZ/2) {
            timeout = MAX_SHOTTIME-bb->elapsedshot;
        } else {
            timeout = HZ/2;
        }

        /* handle button events */
        buttonres = bubbles_handlebuttons(bb, false, timeout);
        if(buttonres != BB_NONE) return buttonres;

        /* handle timing */
        bb->elapsedshot = *rb->current_tick-bb->startedshot;

        if(bb->elapsedshot > MAX_SHOTTIME) {
            bb->elapsedlvl += bb->elapsedshot;
            bb->elapsedshot = 0;
            buttonres = bubbles_fire(bb);
            if(buttonres != BB_NONE) return buttonres;
            buttonres = bubbles_checklevel(bb);
            if(buttonres != BB_NONE) return buttonres;
            bb->startedshot = *rb->current_tick;
        }
    }
}

/*****************************************************************************
* plugin entry point.
******************************************************************************/
enum plugin_status plugin_start(const void* parameter) {
    static struct game_context bb;
    bool exit = false;
    enum plugin_status ret = PLUGIN_OK;

    (void)parameter;

    /* load files */
    resume = bubbles_loadgame(&bb);
    resume_file = resume;
    bubbles_loaddata();
    highscore_load(SCORE_FILE, highscores, NUM_SCORES);
    rb->lcd_clear_display();

    /* start app */
#if LCD_DEPTH > 1
    rb->lcd_set_backdrop(NULL);
#endif
    rb->lcd_setfont(FONT_SYSFIXED);

    while(!exit) {
        switch(bubbles(&bb)){
            case BB_WIN:
                rb->splash(HZ*2, "You Win!");
                /* record high level */
                highlevel = NUM_LEVELS-1;
                /* record high score */
                bubbles_recordscore(&bb);
                break;

            case BB_LOSE:
                resume = false;
                rb->splash(HZ*2, "Game Over");
                /* record high score */
                bubbles_recordscore(&bb);
                /* fall through to BB_END */

            case BB_END:
                break;

            case BB_USB:
                ret = PLUGIN_USB_CONNECTED;
                exit = true;
                break;

            case BB_QUIT:
                rb->splash(HZ/3, "Saving game data ...");
                bubbles_savegame(&bb);
                bubbles_savedata();
                highscore_save(SCORE_FILE, highscores, NUM_SCORES);
                /* fall through */

            case BB_QUIT_WITHOUT_SAVING:
                exit = true;
                break;

            default:
                break;
        }
    }
    rb->lcd_setfont(FONT_UI);
    return ret;
}
