1 /*****************************************************************************
2 * puzzle.c : Puzzle game
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, 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>
37 #include "filter_picture.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 #define ROWS_TEXT N_("Number of puzzle rows")
43 #define ROWS_LONGTEXT N_("Number of puzzle rows")
44 #define COLS_TEXT N_("Number of puzzle columns")
45 #define COLS_LONGTEXT N_("Number of puzzle columns")
46 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
47 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
49 #define CFG_PREFIX "puzzle-"
51 static int Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
55 set_description( N_("Puzzle interactive game video filter") )
56 set_shortname( N_( "Puzzle" ))
57 set_capability( "video filter2", 0 )
58 set_category( CAT_VIDEO )
59 set_subcategory( SUBCAT_VIDEO_VFILTER )
61 add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
62 ROWS_TEXT, ROWS_LONGTEXT, false )
63 add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
64 COLS_TEXT, COLS_LONGTEXT, false )
65 add_bool( CFG_PREFIX "black-slot", 0, NULL,
66 BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, false )
68 set_callbacks( Open, Close )
72 /*****************************************************************************
74 *****************************************************************************/
75 static const char *const ppsz_filter_options[] = {
76 "rows", "cols", "black-slot", NULL
79 static picture_t *Filter( filter_t *, picture_t * );
80 static int Mouse( filter_t *, vlc_mouse_t *, const vlc_mouse_t *, const vlc_mouse_t * );
82 static bool IsFinished( filter_sys_t * );
83 static void Shuffle( filter_sys_t * );
84 static int PuzzleCallback( vlc_object_t *, char const *,
85 vlc_value_t, vlc_value_t, void * );
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 vlc_mutex_init( &p_sys->lock );
154 p_sys->change.i_rows =
155 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
156 p_sys->change.i_cols =
157 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
158 p_sys->change.b_blackslot =
159 var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" );
160 p_sys->b_change = true;
162 var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
163 var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
164 var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
166 p_filter->pf_video_filter = Filter;
167 p_filter->pf_mouse = Mouse;
175 static void Close( vlc_object_t *p_this )
177 filter_t *p_filter = (filter_t *)p_this;
178 filter_sys_t *p_sys = p_filter->p_sys;
180 var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
181 var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
182 var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
184 vlc_mutex_destroy( &p_sys->lock );
185 free( p_sys->pi_order );
193 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
195 filter_sys_t *p_sys = p_filter->p_sys;
197 picture_t *p_outpic = filter_NewPicture( p_filter );
200 picture_Release( p_pic );
205 vlc_mutex_lock( &p_sys->lock );
206 if( p_sys->b_change )
208 p_sys->i_rows = p_sys->change.i_rows;
209 p_sys->i_cols = p_sys->change.i_cols;
210 p_sys->b_blackslot = p_sys->change.b_blackslot;
211 p_sys->b_change = false;
215 vlc_mutex_unlock( &p_sys->lock );
218 const int i_rows = p_sys->i_rows;
219 const int i_cols = p_sys->i_cols;
222 for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
224 const plane_t *p_in = &p_pic->p[i_plane];
225 const int i_pitch = p_in->i_pitch;
226 plane_t *p_out = &p_outpic->p[i_plane];
228 for( int i = 0; i < i_cols * i_rows; i++ )
230 int i_col = i % i_cols;
231 int i_row = i / i_cols;
232 int i_ocol = p_sys->pi_order[i] % i_cols;
233 int i_orow = p_sys->pi_order[i] / i_cols;
234 int i_last_row = i_row + 1;
235 i_orow *= p_in->i_lines / i_rows;
236 i_row *= p_in->i_lines / i_rows;
237 i_last_row *= p_in->i_lines / i_rows;
239 if( p_sys->b_blackslot && p_sys->b_finished && i == p_sys->i_selected )
241 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
242 for( ; i_row < i_last_row; i_row++, i_orow++ )
244 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
245 color, i_pitch / i_cols );
250 for( ; i_row < i_last_row; i_row++, i_orow++ )
252 memcpy( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
253 p_in->p_pixels + i_orow * i_pitch + i_ocol * i_pitch / i_cols,
260 if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
262 const plane_t *p_in = &p_pic->p[Y_PLANE];
263 const int i_pitch = p_in->i_pitch;
264 plane_t *p_out = &p_outpic->p[Y_PLANE];
266 int i_col = p_sys->i_selected % i_cols;
267 int i_row = p_sys->i_selected / i_cols;
268 int i_last_row = i_row + 1;
269 i_row *= p_in->i_lines / i_rows;
270 i_last_row *= p_in->i_lines / i_rows;
271 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
272 0xff, i_pitch / i_cols );
273 for( ; i_row < i_last_row; i_row++ )
275 p_out->p_pixels[ i_row * i_pitch
276 + i_col * i_pitch / i_cols ] = 0xff;
277 p_out->p_pixels[ i_row * i_pitch
278 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
281 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
282 0xff, i_pitch / i_cols );
285 if( p_sys->b_finished )
287 plane_t *p_out = &p_outpic->p[Y_PLANE];
288 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
290 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
292 if( shuffle_button[i][j] == '.' )
293 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
298 return CopyInfoAndRelease( p_outpic, p_pic );
301 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
302 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
304 filter_sys_t *p_sys = p_filter->p_sys;
305 const video_format_t *p_fmt = &p_filter->fmt_in.video;
307 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
308 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
312 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
314 if( p_sys->b_finished )
317 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
319 p_sys->b_change = true;
324 /* This is the only case where we can forward the mouse */
333 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
334 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
335 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
337 if( p_sys->i_selected == -1 )
339 p_sys->i_selected = i_pos;
341 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
343 p_sys->i_selected = -1;
345 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
346 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
347 || p_sys->i_selected == i_pos + p_sys->i_cols
348 || p_sys->i_selected == i_pos - p_sys->i_cols )
350 int a = p_sys->pi_order[ p_sys->i_selected ];
351 p_sys->pi_order[ p_sys->i_selected ] = p_sys->pi_order[ i_pos ];
352 p_sys->pi_order[ i_pos ] = a;
354 if( p_sys->b_blackslot )
355 p_sys->i_selected = i_pos;
357 p_sys->i_selected = -1;
359 p_sys->b_finished = IsFinished( p_sys );
364 /*****************************************************************************
366 *****************************************************************************/
367 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
368 vlc_value_t oldval, vlc_value_t newval,
371 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
372 filter_sys_t *p_sys = (filter_sys_t *)p_data;
374 vlc_mutex_lock( &p_sys->lock );
375 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
377 p_sys->change.i_rows = __MAX( 1, newval.i_int );
379 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
381 p_sys->change.i_cols = __MAX( 1, newval.i_int );
383 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
385 p_sys->change.b_blackslot = newval.b_bool;
387 p_sys->b_change = true;
388 vlc_mutex_unlock( &p_sys->lock );
393 static bool IsFinished( filter_sys_t *p_sys )
395 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
397 if( i != p_sys->pi_order[i] )
403 static bool IsValid( filter_sys_t *p_sys )
405 const int i_count = p_sys->i_cols * p_sys->i_rows;
407 if( p_sys->b_blackslot )
411 for( int i = 0; i < i_count; i++ )
413 if( p_sys->pi_order[i] == i_count - 1 )
415 d += i / p_sys->i_cols + 1;
418 for( int j = i+1; j < i_count; j++ )
420 if( p_sys->pi_order[j] == i_count - 1 )
422 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
429 static void Shuffle( filter_sys_t *p_sys )
431 const int i_count = p_sys->i_cols * p_sys->i_rows;
433 free( p_sys->pi_order );
435 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
438 for( int i = 0; i < i_count; i++ )
439 p_sys->pi_order[i] = -1;
441 for( int c = 0; c < i_count; )
443 int i = rand() % i_count;
444 if( p_sys->pi_order[i] == -1 )
445 p_sys->pi_order[i] = c++;
447 p_sys->b_finished = IsFinished( p_sys );
449 } while( p_sys->b_finished || IsValid( p_sys ) );
451 if( p_sys->b_blackslot )
453 for( int i = 0; i < i_count; i++ )
455 if( p_sys->pi_order[i] == i_count - 1 )
457 p_sys->i_selected = i;
464 p_sys->i_selected = -1;