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, 2, 16, NULL,
62 ROWS_TEXT, ROWS_LONGTEXT, false )
63 add_integer_with_range( CFG_PREFIX "cols", 4, 2, 16, NULL,
64 COLS_TEXT, COLS_LONGTEXT, false )
65 add_bool( CFG_PREFIX "black-slot", false, 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 * );
102 #define SHUFFLE_WIDTH 81
103 #define SHUFFLE_HEIGHT 13
104 static const char *shuffle_button[] =
106 ".................................................................................",
107 ".............. ............................ ........ ...... ...............",
108 ".............. ........................... ......... ........ ...............",
109 ".............. ........................... ......... ........ ...............",
110 ".. ....... . ....... .... ...... ...... ...... ........ ...",
111 ". .... ...... ... ...... .... ....... ......... ........ ....... .. ..",
112 ". ........... .... ...... .... ....... ......... ........ ...... .... .",
113 ". ....... .... ...... .... ....... ......... ........ ...... .",
114 ".. ...... .... ...... .... ....... ......... ........ ...... .......",
115 "...... ...... .... ...... .... ....... ......... ........ ...... .......",
116 ". .... ...... .... ...... ... ....... ......... ........ ....... .... .",
117 ".. ....... .... ....... . ....... ......... ........ ........ ..",
118 "................................................................................."
125 static int Open( vlc_object_t *p_this )
127 filter_t *p_filter = (filter_t *)p_this;
131 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
133 msg_Err( p_filter, "Input and output format does not match" );
137 /* Allocate structure */
138 p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
142 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
145 p_sys->pi_order = NULL;
147 vlc_mutex_init( &p_sys->lock );
148 p_sys->i_rows = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "rows" );
149 p_sys->i_cols = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "cols" );
150 p_sys->b_blackslot = var_CreateGetBoolCommand( p_filter, CFG_PREFIX "black-slot" );
151 p_sys->b_change = true;
153 var_AddCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
154 var_AddCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
155 var_AddCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
157 p_filter->pf_video_filter = Filter;
158 p_filter->pf_mouse = Mouse;
166 static void Close( vlc_object_t *p_this )
168 filter_t *p_filter = (filter_t *)p_this;
169 filter_sys_t *p_sys = p_filter->p_sys;
171 var_DelCallback( p_filter, CFG_PREFIX "rows", PuzzleCallback, p_sys );
172 var_DelCallback( p_filter, CFG_PREFIX "cols", PuzzleCallback, p_sys );
173 var_DelCallback( p_filter, CFG_PREFIX "black-slot", PuzzleCallback, p_sys );
175 vlc_mutex_destroy( &p_sys->lock );
176 free( p_sys->pi_order );
184 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
186 filter_sys_t *p_sys = p_filter->p_sys;
188 picture_t *p_outpic = filter_NewPicture( p_filter );
191 picture_Release( p_pic );
196 vlc_mutex_lock( &p_sys->lock );
197 if( p_sys->b_change )
199 p_sys->b_change = false;
202 vlc_mutex_unlock( &p_sys->lock );
205 const int i_rows = p_sys->i_rows;
206 const int i_cols = p_sys->i_cols;
209 for( int i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
211 const plane_t *p_in = &p_pic->p[i_plane];
212 const int i_pitch = p_in->i_pitch;
213 plane_t *p_out = &p_outpic->p[i_plane];
215 for( int i = 0; i < i_cols * i_rows; i++ )
217 int i_col = i % i_cols;
218 int i_row = i / i_cols;
219 int i_ocol = p_sys->pi_order[i] % i_cols;
220 int i_orow = p_sys->pi_order[i] / i_cols;
221 int i_last_row = i_row + 1;
222 i_orow *= p_in->i_lines / i_rows;
223 i_row *= p_in->i_lines / i_rows;
224 i_last_row *= p_in->i_lines / i_rows;
226 if( p_sys->b_blackslot && !p_sys->b_finished && i == p_sys->i_selected )
228 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
229 for( ; i_row < i_last_row; i_row++, i_orow++ )
231 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
232 color, i_pitch / i_cols );
237 for( ; i_row < i_last_row; i_row++, i_orow++ )
239 memcpy( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
240 p_in->p_pixels + i_orow * i_pitch + i_ocol * i_pitch / i_cols,
247 if( p_sys->i_selected != -1 && !p_sys->b_blackslot )
249 const plane_t *p_in = &p_pic->p[Y_PLANE];
250 const int i_pitch = p_in->i_pitch;
251 plane_t *p_out = &p_outpic->p[Y_PLANE];
253 int i_col = p_sys->i_selected % i_cols;
254 int i_row = p_sys->i_selected / i_cols;
255 int i_last_row = i_row + 1;
256 i_row *= p_in->i_lines / i_rows;
257 i_last_row *= p_in->i_lines / i_rows;
258 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
259 0xff, i_pitch / i_cols );
260 for( ; i_row < i_last_row; i_row++ )
262 p_out->p_pixels[ i_row * i_pitch
263 + i_col * i_pitch / i_cols ] = 0xff;
264 p_out->p_pixels[ i_row * i_pitch
265 + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
268 memset( p_out->p_pixels + i_row * i_pitch + i_col * i_pitch / i_cols,
269 0xff, i_pitch / i_cols );
272 if( p_sys->b_finished )
274 plane_t *p_out = &p_outpic->p[Y_PLANE];
275 for( int i = 0; i < SHUFFLE_HEIGHT; i++ )
277 for( int j = 0; j < SHUFFLE_WIDTH; j++ )
279 if( shuffle_button[i][j] == '.' )
280 p_out->p_pixels[ i * p_out->i_pitch + j ] = 0xff;
285 return CopyInfoAndRelease( p_outpic, p_pic );
288 static int Mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
289 const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
291 filter_sys_t *p_sys = p_filter->p_sys;
292 const video_format_t *p_fmt = &p_filter->fmt_in.video;
294 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt->i_width ||
295 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt->i_height )
299 const bool b_clicked = vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT );
301 if( p_sys->b_finished )
304 p_new->i_x < SHUFFLE_WIDTH && p_new->i_y < SHUFFLE_HEIGHT )
306 p_sys->b_change = true;
311 /* This is the only case where we can forward the mouse */
320 const int i_pos_x = p_new->i_x * p_sys->i_cols / p_fmt->i_width;
321 const int i_pos_y = p_new->i_y * p_sys->i_rows / p_fmt->i_height;
322 const int i_pos = i_pos_y * p_sys->i_cols + i_pos_x;
324 if( p_sys->i_selected == -1 )
326 p_sys->i_selected = i_pos;
328 else if( p_sys->i_selected == i_pos && !p_sys->b_blackslot )
330 p_sys->i_selected = -1;
332 else if( ( p_sys->i_selected == i_pos + 1 && p_sys->i_selected%p_sys->i_cols != 0 )
333 || ( p_sys->i_selected == i_pos - 1 && i_pos % p_sys->i_cols != 0 )
334 || p_sys->i_selected == i_pos + p_sys->i_cols
335 || p_sys->i_selected == i_pos - p_sys->i_cols )
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 if( p_sys->b_blackslot )
342 p_sys->i_selected = i_pos;
344 p_sys->i_selected = -1;
346 p_sys->b_finished = IsFinished( p_sys );
351 /*****************************************************************************
353 *****************************************************************************/
354 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
355 vlc_value_t oldval, vlc_value_t newval,
358 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
359 filter_sys_t *p_sys = (filter_sys_t *)p_data;
361 vlc_mutex_lock( &p_sys->lock );
362 if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
364 p_sys->i_rows = __MAX( 1, newval.i_int );
366 else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
368 p_sys->i_cols = __MAX( 1, newval.i_int );
370 else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
372 p_sys->b_blackslot = newval.b_bool;
374 p_sys->b_change = true;
375 vlc_mutex_unlock( &p_sys->lock );
380 static bool IsFinished( filter_sys_t *p_sys )
382 for( int i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
384 if( i != p_sys->pi_order[i] )
390 static bool IsValid( filter_sys_t *p_sys )
392 const int i_count = p_sys->i_cols * p_sys->i_rows;
394 if( p_sys->b_blackslot )
398 for( int i = 0; i < i_count; i++ )
400 if( p_sys->pi_order[i] == i_count - 1 )
402 d += i / p_sys->i_cols + 1;
405 for( int j = i+1; j < i_count; j++ )
407 if( p_sys->pi_order[j] == i_count - 1 )
409 if( p_sys->pi_order[i] > p_sys->pi_order[j] )
416 static void Shuffle( filter_sys_t *p_sys )
418 const int i_count = p_sys->i_cols * p_sys->i_rows;
420 free( p_sys->pi_order );
422 p_sys->pi_order = calloc( i_count, sizeof(*p_sys->pi_order) );
425 for( int i = 0; i < i_count; i++ )
426 p_sys->pi_order[i] = -1;
428 for( int c = 0; c < i_count; )
430 int i = rand() % i_count;
431 if( p_sys->pi_order[i] == -1 )
432 p_sys->pi_order[i] = c++;
434 p_sys->b_finished = IsFinished( p_sys );
436 } while( p_sys->b_finished || IsValid( p_sys ) );
438 if( p_sys->b_blackslot )
440 for( int i = 0; i < i_count; i++ )
442 if( p_sys->pi_order[i] == i_count - 1 )
444 p_sys->i_selected = i;
451 p_sys->i_selected = -1;