1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2009 VLC authors and VideoLAN
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
38 #include "filter_picture.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 #define ROWS_TEXT N_("Number of puzzle rows")
44 #define ROWS_LONGTEXT N_("Number of puzzle rows")
45 #define COLS_TEXT N_("Number of puzzle columns")
46 #define COLS_LONGTEXT N_("Number of puzzle columns")
47 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
48 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
50 #define CFG_PREFIX "puzzle-"
52 static int Open ( vlc_object_t * );
53 static void Close( vlc_object_t * );
56 set_description( N_("Puzzle interactive game video filter") )
57 set_shortname( N_( "Puzzle" ))
58 set_capability( "video filter2", 0 )
59 set_category( CAT_VIDEO )
60 set_subcategory( SUBCAT_VIDEO_VFILTER )
62 add_integer_with_range( CFG_PREFIX "rows", 4, 2, 16,
63 ROWS_TEXT, ROWS_LONGTEXT, false )
64 add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16,
65 COLS_TEXT, COLS_LONGTEXT, false )
66 add_bool( CFG_PREFIX "black-slot", false,
67 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
69 set_callbacks( Open, Close )
73 /*****************************************************************************
75 *****************************************************************************/
76 static const char *const ppsz_filter_options[] = {
77 "rows", "cols", "black-slot", NULL
80 static picture_t *Filter( filter_t *, picture_t * );
81 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const vlc_mouse_t * );
83 static bool IsFinished( filter_sys_t * );
84 static void Shuffle( filter_sys_t * );
85 static int PuzzleCallback( vlc_object_t *, char const *,
86 vlc_value_t, vlc_value_t, void * );
101 atomic_flag b_uptodate;
102 atomic_bool b_blackslot;
108 #define SHUFFLE_WIDTH 81
109 #define SHUFFLE_HEIGHT 13
110 static const char *shuffle_button[] =
112 ".................................................................................",
113 ".............. ............................ ........ ...... ...............",
114 ".............. ........................... ......... ........ ...............",
115 ".............. ........................... ......... ........ ...............",
116 ".. ....... . ....... .... ...... ...... ...... ........ ...",
117 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
118 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
119 ". ....... .... ...... .... ....... ......... ........ ...... .",
120 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
121 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
122 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
123 ".. ....... .... ....... . ....... ......... ........ ........ ..",
124 "................................................................................."
131 static int Open( vlc_object_t *p_this )
133 filter_t *p_filter = (filter_t *)p_this;
137 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
139 msg_Err( p_filter, "Input and output format does not match" );
143 /* Allocate structure */
144 p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
148 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
151 p_sys->pi_order = NULL;
153 atomic_init( &p_sys->change.i_rows,
154 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" ) );
155 atomic_init( &p_sys->change.i_cols,
156 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" ) );
157 atomic_init( &p_sys->change.b_blackslot,
158 var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" ) );
159 p_sys->change.b_uptodate = ATOMIC_FLAG_INIT;
161 var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
162 var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
163 var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
165 p_filter->pf_video_filter = Filter;
166 p_filter->pf_video_mouse = Mouse;
174 static void Close( vlc_object_t *p_this )
176 filter_t *p_filter = (filter_t *)p_this;
177 filter_sys_t *p_sys = p_filter->p_sys;
179 var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
180 var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
181 var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
183 free( p_sys->pi_order );
191 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
193 filter_sys_t *p_sys = p_filter->p_sys;
195 picture_t *p_outpic = filter_NewPicture( p_filter );
198 picture_Release( p_pic );
203 if( !atomic_flag_test_and_set( &p_sys->change.b_uptodate ) )
205 p_sys->i_rows = atomic_load( &p_sys->change.i_rows );
206 p_sys->i_cols = atomic_load( &p_sys->change.i_cols );
207 p_sys->b_blackslot = atomic_load( &p_sys->change.b_blackslot );
212 const int i_rows = p_sys->i_rows;
213 const int i_cols = p_sys->i_cols;
215 /* Draw each piece of the puzzle at the right place */
216 for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
218 const plane_t *p_in = &p_pic->p[i_plane];
219 plane_t *p_out = &p_outpic->p[i_plane];
221 for( int i = 0; i < i_cols * i_rows; i++ )
223 int i_piece_height = p_out->i_visible_lines / i_rows;
224 int i_piece_width = p_out->i_visible_pitch / i_cols;
226 int i_col = (i % i_cols) * i_piece_width;
227 int i_row = (i / i_cols) * i_piece_height;
228 int i_last_row = i_row + i_piece_height;
230 int i_ocol = (p_sys->pi_order[i] % i_cols) * i_piece_width;
231 int i_orow = (p_sys->pi_order[i] / i_cols) * i_piece_height;
233 if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
235 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
236 for( int r = i_row; r < i_last_row; r++ )
238 memset( p_out->p_pixels + r * p_out->i_pitch + i_col,
239 color, i_piece_width );
244 for( int r = i_row, or = i_orow; r < i_last_row; r++, or++ )
246 memcpy( p_out->p_pixels + r * p_out->i_pitch + i_col,
247 p_in->p_pixels + or * p_in->i_pitch + i_ocol,
252 /* Draw the borders of the selected slot */
253 if( i_plane == 0 && !p_sys->b_blackslot && p_sys->i_selected == i )
255 memset( p_out->p_pixels + i_row * p_out->i_pitch + i_col,
256 0xff, i_piece_width );
257 for( int r = i_row; r < i_last_row; r++ )
259 p_out->p_pixels[r * p_out->i_pitch + i_col + 0 + 0 ] = 0xff;
260 p_out->p_pixels[r * p_out->i_pitch + i_col + i_piece_width - 1 ] = 0xff;
262 memset( p_out->p_pixels + (i_last_row - 1) * p_out->i_pitch + i_col,
263 0xff, i_piece_width );
268 /* Draw the 'Shuffle' button if the puzzle is finished */
269 if( p_sys->b_finished )
271 plane_t *p_out = &p_outpic->p[Y_PLANE];
272 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
274 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
276 if( shuffle_button[i][j] == '.' )
277 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
282 return CopyInfoAndRelease( p_outpic, p_pic );
285 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
286 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
288 filter_sys_t *p_sys = p_filter->p_sys;
289 const video_format_t *p_fmt = &p_filter->fmt_in.video;
291 /* Only take events inside the puzzle erea */
292 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
293 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
297 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
299 /* If the puzzle is finished, shuffle it if needed */
300 if( p_sys->b_finished )
303 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
305 atomic_flag_clear( &p_sys->change.b_uptodate );
310 /* This is the only case where we can forward the mouse */
319 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
320 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
321 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
323 if( p_sys->i_selected == -1 )
325 p_sys->i_selected = i_pos;
327 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
329 p_sys->i_selected = -1;
331 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
332 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
333 || p_sys->i_selected == i_pos + p_sys->i_cols
334 || p_sys->i_selected == i_pos - p_sys->i_cols )
336 /* Swap two pieces */
337 int a = p_sys->pi_order[ p_sys->i_selected ];
338 p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
339 p_sys->pi_order[ i_pos ] = a;
341 p_sys->i_selected = p_sys->b_blackslot ? i_pos : -1;
342 p_sys->b_finished = IsFinished( p_sys );
347 /*****************************************************************************
349 *****************************************************************************/
350 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
351 vlc_value_t oldval, vlc_value_t newval,
354 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
355 filter_sys_t *p_sys = p_data;
357 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
358 atomic_store( &p_sys->change.i_rows, __MAX( 2, newval.i_int ) );
359 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
360 atomic_store( &p_sys->change.i_cols, __MAX( 2, newval.i_int ) );
361 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
362 atomic_store( &p_sys->change.b_blackslot, newval.b_bool );
363 atomic_flag_clear( &p_sys->change.b_uptodate );
368 static bool IsFinished( filter_sys_t *p_sys )
370 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
372 if( i != p_sys->pi_order[i] )
378 static bool IsValid( filter_sys_t *p_sys )
380 const int i_count = p_sys->i_cols * p_sys->i_rows;
382 if( !p_sys->b_blackslot )
386 for( int i = 0; i < i_count; i++ )
388 if( p_sys->pi_order[i] == i_count - 1 )
390 d += i / p_sys->i_cols + 1;
393 for( int j = i+1; j < i_count; j++ )
395 if( p_sys->pi_order[j] == i_count - 1 )
397 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
404 static void Shuffle( filter_sys_t *p_sys )
406 const unsigned i_count = p_sys->i_cols * p_sys->i_rows;
408 free( p_sys->pi_order );
410 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
413 for( unsigned i = 0; i < i_count; i++ )
414 p_sys->pi_order[i] = -1;
416 for( unsigned c = 0; c < i_count; )
418 unsigned i = ((unsigned)vlc_mrand48()) % i_count;
419 if( p_sys->pi_order[i] == -1 )
420 p_sys->pi_order[i] = c++;
422 p_sys->b_finished = IsFinished( p_sys );
424 } while( p_sys->b_finished || !IsValid( p_sys ) );
426 if( p_sys->b_blackslot )
428 for( unsigned i = 0; i < i_count; i++ )
430 if( p_sys->pi_order[i] == (int)i_count - 1 )
432 p_sys->i_selected = i;
439 p_sys->i_selected = -1;