PTdecode/CImg-1.3.0/examples/jawbreaker.cpp

Wed, 05 Aug 2009 17:32:05 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 17:32:05 +0100
changeset 18
fd1c6f6066da
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

updated README

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : jawbreaker.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : A funny game featuring small colored balls.
philpem@5 7 # This file is a part of the CImg Library project.
philpem@5 8 # ( http://cimg.sourceforge.net )
philpem@5 9 #
philpem@5 10 # Copyright : David Tschumperle
philpem@5 11 # ( http://www.greyc.ensicaen.fr/~dtschump/ )
philpem@5 12 #
philpem@5 13 # License : CeCILL v2.0
philpem@5 14 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
philpem@5 15 #
philpem@5 16 # This software is governed by the CeCILL license under French law and
philpem@5 17 # abiding by the rules of distribution of free software. You can use,
philpem@5 18 # modify and/ or redistribute the software under the terms of the CeCILL
philpem@5 19 # license as circulated by CEA, CNRS and INRIA at the following URL
philpem@5 20 # "http://www.cecill.info".
philpem@5 21 #
philpem@5 22 # As a counterpart to the access to the source code and rights to copy,
philpem@5 23 # modify and redistribute granted by the license, users are provided only
philpem@5 24 # with a limited warranty and the software's author, the holder of the
philpem@5 25 # economic rights, and the successive licensors have only limited
philpem@5 26 # liability.
philpem@5 27 #
philpem@5 28 # In this respect, the user's attention is drawn to the risks associated
philpem@5 29 # with loading, using, modifying and/or developing or reproducing the
philpem@5 30 # software by the user in light of its specific status of free software,
philpem@5 31 # that may mean that it is complicated to manipulate, and that also
philpem@5 32 # therefore means that it is reserved for developers and experienced
philpem@5 33 # professionals having in-depth computer knowledge. Users are therefore
philpem@5 34 # encouraged to load and test the software's suitability as regards their
philpem@5 35 # requirements in conditions enabling the security of their systems and/or
philpem@5 36 # data to be ensured and, more generally, to use and operate it in the
philpem@5 37 # same conditions as regards security.
philpem@5 38 #
philpem@5 39 # The fact that you are presently reading this means that you have had
philpem@5 40 # knowledge of the CeCILL license and that you accept its terms.
philpem@5 41 #
philpem@5 42 */
philpem@5 43
philpem@5 44 #include "CImg.h"
philpem@5 45 using namespace cimg_library;
philpem@5 46
philpem@5 47 // The lines below are necessary when using a non-standard compiler as visualcpp6.
philpem@5 48 #ifdef cimg_use_visualcpp6
philpem@5 49 #define std
philpem@5 50 #endif
philpem@5 51 #ifdef max
philpem@5 52 #undef max
philpem@5 53 #endif
philpem@5 54
philpem@5 55 // Start main procedure
philpem@5 56 //----------------------
philpem@5 57 int main(int argc, char **argv) {
philpem@5 58
philpem@5 59 // Display help (if option '-h' or '--help' specified) and retrieve program arguments
philpem@5 60 cimg_usage("A small and funny game featuring colored balls.\n (by David Tschumperle).");
philpem@5 61 const char *score_file = cimg_option("-s",(char*)0,"Specify score file to use (0=default file).");
philpem@5 62 cimg_help("\n"
philpem@5 63 "** Quick Help *********************************************************\n\n"
philpem@5 64 "Goal : Delete the board by clicking on groups of adjacent colored balls\n"
philpem@5 65 " (a group is made of at least two balls with the same color).\n"
philpem@5 66 " Suppressing large sets gives higher scores.\n\n"
philpem@5 67 "In-game keys : - BACKSPACE or SPACE = Undo last move\n"
philpem@5 68 " - CTRL+F = Toggle fullscreen mode\n"
philpem@5 69 " - ESC = Quit application\n"
philpem@5 70 " - Q = End current game\n\n"
philpem@5 71 "*********************************************************************");
philpem@5 72
philpem@5 73 // Load score file if available
philpem@5 74 CImgList<unsigned int> score_history;
philpem@5 75 char filename_history[1024];
philpem@5 76 std::sprintf(filename_history,"%s%s",score_file?"":cimg::temporary_path(),score_file?score_file:"/jawbreaker.score");
philpem@5 77 std::FILE *file = std::fopen(filename_history,"r");
philpem@5 78 if (file) { std::fclose(file); score_history = CImg<unsigned int>::get_load_dlm(filename_history).get_split('y'); }
philpem@5 79
philpem@5 80 // Create ball graphics
philpem@5 81 const unsigned int W = 12, H = 14, Wi = (W<<5), Hi = (H<<5);
philpem@5 82 unsigned int score = 0, previous_score = 0, shape_score = 0,
philpem@5 83 best_score = score_history?score_history.max():0U;
philpem@5 84
philpem@5 85 const CImg<> colors(3,7,1,1, 255,255,255, 205,0,230, 0,235,0, 235,255,0, 235,0,0, 0,128,255, 450,350,300);
philpem@5 86 const unsigned char
philpem@5 87 white[] = { 255,255,255 }, orange[] = { 255,128,64 }, yellow[] = { 255,255,64 }, red[] = { 255,64,64 }, six = 6;
philpem@5 88 CImgList<> balls0(7,32,32,1,3,0);
philpem@5 89 cimglist_for(balls0,l) if (l) {
philpem@5 90 balls0[l].draw_circle(16,16,14,colors.ptr(0,l));
philpem@5 91 cimg_forXYV(balls0[l],x,y,k) if (balls0(l,x,y,k)) (balls0(l,x,y,k)*=(32-x+y)/60.0f)+=20;
philpem@5 92 balls0[l].draw_circle(16,16,14,colors.ptr(0,l),0.5f,~0U).
philpem@5 93 draw_circle(20,10,5,colors,0.2f).draw_circle(22,8,2,colors,0.4f).cut(0,255);
philpem@5 94 }
philpem@5 95
philpem@5 96 // Create background graphics
philpem@5 97 CImgList<unsigned char> balls(balls0);
philpem@5 98 CImg<unsigned char>
philpem@5 99 mask = balls[1].get_cut(0,1).channel(0).dilate(3),
philpem@5 100 background = CImg<unsigned char>(Wi,Hi,1,3,0).
philpem@5 101 noise(255,1).blur(6,20,0).equalize(100,0,255).blur(2,4,0);
philpem@5 102 background.get_shared_channel(0)/=4; background.get_shared_channel(1)/=8; background.get_shared_channel(2)/=2;
philpem@5 103
philpem@5 104 // Begin user-interaction loop.
philpem@5 105 CImg<unsigned char> board, previous_board, selected_board, shape, img(background);
philpem@5 106 CImgDisplay disp(img.dimx(),img.dimy(),"Jawbreaker",0);
philpem@5 107 bool redraw = true, gameover = false, title = true;
philpem@5 108 for (float opac = 0.0f; !disp.is_closed; ) {
philpem@5 109
philpem@5 110 // Init board
philpem@5 111 if (!board) {
philpem@5 112 (++((board.assign(W,H,1,1,5).noise(5,1))%=5)).get_shared_line(0).fill(0);
philpem@5 113 opac = (float)(score = previous_score = shape_score = 0);
philpem@5 114 gameover = false; redraw = title = true;
philpem@5 115 previous_board = board;
philpem@5 116 }
philpem@5 117
philpem@5 118 // Draw graphical board
philpem@5 119 if (redraw) {
philpem@5 120 (img=background).draw_text(2,2,"Score : %u",yellow,0,0.7f,24,score).
philpem@5 121 draw_text(Wi-90,2,"Best : %u",orange,0,0.9f,16,best_score);
philpem@5 122 if (selected_board) {
philpem@5 123 cimg_forXY(selected_board,x,y) if (selected_board(x,y))
philpem@5 124 img.draw_image(x<<5,y<<5,balls[selected_board(x,y)],mask);
philpem@5 125 } else cimg_forXY(board,x,y) if (board(x,y)) img.draw_image(x<<5,y<<5,balls[board(x,y)],mask);
philpem@5 126 if (title) {
philpem@5 127 CImg<unsigned char> text1, text2;
philpem@5 128 text1.draw_text(0,0,"- Jawbreaker -",white,0,1,48);
philpem@5 129 text2.draw_text(0,0,"Press button to start",yellow,0,1,24);
philpem@5 130 (img/=2).draw_image((Wi-text1.dimx())/2,
philpem@5 131 (Hi-text1.dimy())/2,
philpem@5 132 text1,text1.get_dilate(7),1,255).
philpem@5 133 draw_image((Wi-text2.dimx())/2,
philpem@5 134 (Hi+text1.dimy()+10)/2,
philpem@5 135 text2,text2.get_dilate(5),0.7f,255);
philpem@5 136 for (float i=1; i<10 && !disp.is_keyESC; i+=0.25)
philpem@5 137 disp.display(img.get_crop((int)(Wi*(0.5f-i*i/200.0f)),(int)(Hi*(0.5f-i*i*i*i/20000.0f)),
philpem@5 138 (int)(Wi*(0.5f+i*i/200.0f)),(int)(Hi*(0.5f+i*i*i*i/20000.0f)))).wait(20);
philpem@5 139 }
philpem@5 140 }
philpem@5 141 if ((opac-=0.02f)>0) disp.display((+img).draw_text(disp.mouse_x-8,disp.mouse_y-54+(int)(30*opac),"+%u",
philpem@5 142 white,0,(float)std::sqrt(opac),32,shape_score)).wait(20);
philpem@5 143 else { if (redraw) { disp.display(img); redraw = false; } else disp.wait(); }
philpem@5 144
philpem@5 145 // Handle key and window events
philpem@5 146 if (disp.is_resized) disp.resize(disp);
philpem@5 147 if (disp.is_keyBACKSPACE || disp.is_keySPACE) {
philpem@5 148 board = previous_board; score = previous_score; selected_board.assign(); redraw = true; disp.key = 0;
philpem@5 149 }
philpem@5 150 if (disp.is_keyQ) { gameover = true; disp.key = 0; }
philpem@5 151 if (disp.is_keyESC) disp.close();
philpem@5 152 if (disp.is_keyCTRLLEFT && disp.key==cimg::keyF) disp.toggle_fullscreen().display(img);
philpem@5 153
philpem@5 154 // Handle ball selection and removal
philpem@5 155 const int x = disp.mouse_x*board.dimx()/disp.dimx(), y = disp.mouse_y*board.dimy()/disp.dimy();
philpem@5 156 if (disp.button&1 && x>=0 && y>=0) {
philpem@5 157 if (title) { title = false; redraw = true; } else {
philpem@5 158 disp.button=0;
philpem@5 159 if (!board(x,y)) { selected_board.assign(); redraw = true; }
philpem@5 160 else {
philpem@5 161 if (!selected_board || selected_board(x,y)!=6) {
philpem@5 162 (selected_board=board).draw_fill(x,y,0,&six,1,shape);
philpem@5 163 if ((shape_score=(unsigned int)shape.sum())<2) selected_board.assign();
philpem@5 164 else { (shape_score-=1)*=shape_score; opac = 1.0f; redraw = true; }
philpem@5 165 } else {
philpem@5 166 selected_board.assign();
philpem@5 167 previous_board = board;
philpem@5 168 previous_score = score;
philpem@5 169 score += shape_score;
philpem@5 170 board&=--shape;
philpem@5 171 redraw = true;
philpem@5 172
philpem@5 173 // Handle board modification due to ball removal
philpem@5 174 for (int pmax = board.dimx(), p=0; p<pmax; ++p) {
philpem@5 175 for (int q = board.dimy()-1, qs = q; q>=0; --q) {
philpem@5 176 while (!board(p,qs)) --qs;
philpem@5 177 board(p,q) = (qs>=0?board(p,qs--):0);
philpem@5 178 }
philpem@5 179 if (!board(p,board.dimy()-1)) {
philpem@5 180 board.draw_image(p,board.get_crop(p,0,board.dimx()-1,board.dimy()-1).translate(1));
philpem@5 181 if (p<pmax) { p--; pmax--; }
philpem@5 182 }
philpem@5 183 }
philpem@5 184
philpem@5 185 // Test possible end of the game
philpem@5 186 gameover = true;
philpem@5 187 cimg_forXY(board,x,y)
philpem@5 188 if (board(x,y) && ((y && board(x,y)==board(x,y-1)) || (x && board(x,y)==board(x-1,y)))) gameover = false;
philpem@5 189 }
philpem@5 190 }
philpem@5 191 }
philpem@5 192 disp.button = 0;
philpem@5 193 }
philpem@5 194
philpem@5 195 // If game is over...
philpem@5 196 if (gameover && opac<=0) {
philpem@5 197 CImg<unsigned char> text1, text2, text3, text4, text5, text6;
philpem@5 198 text1.draw_text(0,0,"Game Over !",white,0,1,48);
philpem@5 199 const unsigned int remaining_balls = (unsigned int)board.get_cut(0,1).sum();
philpem@5 200 if (remaining_balls<8) {
philpem@5 201 const unsigned int bonus = (22-2*remaining_balls)*10;
philpem@5 202 score += bonus;
philpem@5 203 text2.draw_text(0,0,"Jawbreaker Bonus : +%u",white,0,1,24,bonus);
philpem@5 204 }
philpem@5 205 score_history.insert(CImg<unsigned int>::vector(score));
philpem@5 206 text3.draw_text(0,0,"Final score : %u",yellow,0,1,24,score);
philpem@5 207 text4.draw_text(0,0,score>best_score?"** New record ! **":"Best score : %u",
philpem@5 208 orange,0,1,24,score>best_score?score:best_score);
philpem@5 209 text5.draw_text(0,0,"Average score : %u",red,0,1,24,
philpem@5 210 score_history?(unsigned int)score_history.mean():0U);
philpem@5 211 text6.draw_text(0,0,"Games played : %u",red,0,1,24,score_history.size);
philpem@5 212 if (score>best_score) best_score = score;
philpem@5 213
philpem@5 214 unsigned int yt = (Hi-text1.dimy())/2-20;
philpem@5 215 (img/=2).draw_image((Wi-text1.dimx())/2,yt,text1,text1.get_dilate(7),1,255); yt+=80;
philpem@5 216 if (text2) { img.draw_image((Wi-text2.dimx())/2,yt,text2,text2.get_dilate(5),1,255); yt+=25; }
philpem@5 217 img.draw_image((Wi-text3.dimx())/2,yt,text3,text3.get_dilate(5),1,255).
philpem@5 218 draw_image((Wi-text4.dimx())/2,yt+25,text4,text4.get_dilate(5),1,255).
philpem@5 219 draw_image((Wi-text5.dimx())/2,yt+50,text5,text5.get_dilate(5),1,255).
philpem@5 220 draw_image((Wi-text6.dimx())/2,yt+75,text6,text6.get_dilate(5),1,255).display(disp);
philpem@5 221 for (disp.button = disp.key = 0; !disp.is_closed && !disp.key && !disp.button; disp.wait())
philpem@5 222 if (disp.is_resized) disp.resize(disp);
philpem@5 223 disp.button = disp.key = 0;
philpem@5 224 board.assign();
philpem@5 225 for (float i=10; i>0 && !disp.is_keyESC; i-=0.25)
philpem@5 226 disp.display(img.get_crop((int)(Wi*(0.5f-i*i*i*i/20000.0f)),(int)(Hi*(0.5f-i*i/200.0f)),
philpem@5 227 (int)(Wi*(0.5f+i*i*i*i/20000.0f)),(int)(Hi*(0.5f+i*i/200.0f)))).wait(20);
philpem@5 228 }
philpem@5 229 }
philpem@5 230
philpem@5 231 // Save score history if possible, and exit.
philpem@5 232 if (score_history) {
philpem@5 233 file = std::fopen(filename_history,"w");
philpem@5 234 if (file) { std::fclose(file); score_history.get_append('y').save_dlm(filename_history); }
philpem@5 235 }
philpem@5 236 return 0;
philpem@5 237 }