5 * Copyright (C) 2003, 2007, 2008 Thomas Perl <thp@perli.net>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
34 GameState
*gamestate_new() {
38 GameState
template = {
39 { 0, 0, 0.0, 0.0, 0.0 },
41 { GAME_X_MIN
-RACKET_X_MID
*2, GAME_Y_MID
, 0, 0, 1, DESIRE_NORMAL
, PLAYER_TYPE_HUMAN
, GAME_Y_MID
, false, 0, {0}, 0, 0, PLAYER_ACCEL_DEFAULT
, true },
42 { GAME_X_MAX
+RACKET_X_MID
*2, GAME_Y_MID
, 0, 0, 0, DESIRE_NORMAL
, PLAYER_TYPE_AI
, GAME_Y_MID
, false, 0, {0}, 0, 0, PLAYER_ACCEL_DEFAULT
, true },
47 "welcome to tennix " VERSION
,
75 s
= (GameState
*)malloc(sizeof(GameState
));
76 if (s
== NULL
) abort();
78 memcpy(s
, &template, sizeof(GameState
));
83 for( x
= 0; x
<NGRAM_STEPS
; x
++) {
84 for( y
= 0; y
<NGRAM_STEPS
; y
++) {
85 for( z
= 0; z
<NGRAM_STEPS
; z
++) {
86 s
->ngram
[x
][y
][z
] = 1;
95 void gameloop(GameState
*s
) {
96 strcpy(s
->game_score_str
, format_game(s
));
97 strcpy(s
->sets_score_str
, format_sets(s
));
98 s
->text_changed
= true;
100 Uint32 ot
= SDL_GetTicks();
102 Uint32 dt
= GAME_TICKS
;
104 Uint32 accumulator
= 0;
107 #ifdef ENABLE_FPS_LIMIT
108 Uint32 ft
, frames
; /* frame timer and frames */
112 play_sample_background(SOUND_RAIN
);
114 play_sample_loop(SOUND_AUDIENCE
);
115 /* Reset the court type, so it is redrawn on first display */
116 s
->old_court_type
= -1;
118 #ifdef ENABLE_FPS_LIMIT
132 while( accumulator
>= dt
) {
135 s
->windtime
+= s
->wind
*dt
;
138 if( s
->was_stopped
) {
140 s
->was_stopped
= false;
143 if (s
->timelimit
!= 0 && s
->time
>= s
->timelimit
) {
147 #ifdef ENABLE_FPS_LIMIT
148 while (frames
*1000.0/((float)(SDL_GetTicks()-ft
+1))>(float)(DEFAULT_FPS
)) {
160 stop_sample(SOUND_AUDIENCE
);
161 stop_sample(SOUND_RAIN
);
164 bool step( GameState
* s
) {
168 if( get_phase( s
) < 1.0) {
169 if( !s
->ground
.jump
) {
170 s
->play_sound
= SOUND_GROUND
;
172 if( IS_OUT_Y( s
->ball
.y
)) {
173 /* out - responsibilities stay the same */
175 s
->play_sound
= SOUND_OUT
;
176 s
->referee
= REFEREE_OUT
;
178 /* not out - responsibilities change */
179 s
->player1
.responsible
= !(s
->player2
.responsible
= !s
->player2
.responsible
);
180 s
->status
= format_status( s
);
181 s
->referee
= REFEREE_NORMAL
;
185 s
->ground
.x
= s
->ball
.x
;
186 s
->ground
.y
= s
->ball
.y
;
188 if( s
->ground
.jump
&& !(s
->time
%5)) s
->ground
.jump
--;
191 if( IS_OUT_X(s
->ball
.x
) || IS_OFFSCREEN_Y(s
->ball
.y
)) {
192 if( IS_OFFSCREEN( s
->ball
.x
, s
->ball
.y
)) {
193 s
->player1_serves
= s
->player1
.responsible
;
195 score_game( s
, s
->player2
.responsible
);
196 strcpy( s
->game_score_str
, format_game( s
));
197 strcpy( s
->sets_score_str
, format_sets( s
));
198 s
->text_changed
= true;
200 if( s
->player1
.responsible
) {
201 if( s
->player1
.type
== PLAYER_TYPE_HUMAN
&& s
->player2
.type
== PLAYER_TYPE_AI
) {
202 s
->status
= "computer scores";
204 s
->status
= "player 2 scores";
206 s
->referee
= REFEREE_PLAYER2
;
208 if( s
->player1
.type
== PLAYER_TYPE_HUMAN
&& s
->player2
.type
== PLAYER_TYPE_AI
) {
209 s
->status
= "player scores";
211 s
->status
= "player 1 scores";
213 s
->referee
= REFEREE_PLAYER1
;
216 game_setup_serve( s
);
217 s
->play_sound
= SOUND_APPLAUSE
;
219 s
->was_stopped
= true;
221 s
->history_is_locked
= 0;
222 s
->ngram_prediction
= 0.0;
224 printf( "-- game reset --\n");
228 if( IS_OUT_X(s
->ball
.x
)) {
229 if( !s
->history_is_locked
&& s
->referee
!= REFEREE_OUT
) {
230 s
->history
[s
->history_size
] = (int)(NGRAM_STEPS
*s
->ball
.y
/HEIGHT
);
232 if( s
->history_size
== 3) {
233 s
->ngram
[s
->history
[0]][s
->history
[1]][s
->history
[2]] += 10;
235 printf( "history: %d, %d, %d\n", s
->history
[0], s
->history
[1], s
->history
[2]);
237 s
->ngram_prediction
= ngram_predictor( s
);
238 s
->history
[0] = s
->history
[1];
239 s
->history
[1] = s
->history
[2];
242 s
->history_is_locked
= true;
244 if( s
->ball
.move_x
<= 0 && IS_NEAR_X( s
->player1
.x
, s
->ball
.x
) && IS_NEAR_Y( s
->player1
.y
, s
->ball
.y
) && s
->player1
.state
&& s
->referee
!= REFEREE_OUT
) {
245 s
->ball
.x
= GAME_X_MIN
;
246 if( s
->player1
.state
== PLAYER_STATE_MAX
) {
247 s
->ball
.move_x
= PLAYER_POWERSHOT
;
249 s
->ball
.move_x
= 2.5 + 2.0*s
->player1
.state
/PLAYER_STATE_MAX
;
251 s
->ball
.move_y
= get_move_y( s
, 1);
252 s
->player2
.responsible
= !(s
->player1
.responsible
= 1);
253 s
->ball
.jump
+= 1.0-2.0*(s
->player1
.state
<5);
254 s
->play_sound
= SOUND_RACKET
;
255 pan_sample(SOUND_RACKET
, 0.4);
256 } else if( s
->ball
.move_x
>= 0 && IS_NEAR_X( s
->player2
.x
, s
->ball
.x
) && IS_NEAR_Y( s
->player2
.y
, s
->ball
.y
) && s
->player2
.state
&& s
->referee
!= REFEREE_OUT
) {
257 s
->ball
.x
= GAME_X_MAX
;
258 if( s
->player2
.state
== PLAYER_STATE_MAX
) {
259 s
->ball
.move_x
= -PLAYER_POWERSHOT
;
261 s
->ball
.move_x
= -(2.5 + 2.0*s
->player2
.state
/PLAYER_STATE_MAX
);
263 s
->ball
.move_y
= get_move_y( s
, 2);
264 s
->player1
.responsible
= !(s
->player2
.responsible
= 1);
265 s
->ball
.jump
+= 1.0-2.0*(s
->player2
.state
<5);
266 s
->play_sound
= SOUND_RACKET
;
267 pan_sample(SOUND_RACKET
, 0.6);
271 s
->history_is_locked
= false;
275 keys
= SDL_GetKeyState( NULL
);
277 case SDL_JOYAXISMOTION
:
278 if (e
.jaxis
.axis
== JOYSTICK_Y_AXIS
) {
279 s
->joystick_y
= JOYSTICK_PERCENTIZE(e
.jaxis
.value
);
280 } else if (e
.jaxis
.axis
== JOYSTICK_X_AXIS
) {
281 s
->joystick_x
= JOYSTICK_PERCENTIZE(e
.jaxis
.value
);
284 case SDL_JOYBUTTONUP
: case SDL_JOYBUTTONDOWN
:
285 if (e
.jbutton
.button
== JOYSTICK_BUTTON_A
) {
286 s
->joystick_a
= (e
.jbutton
.state
== SDL_PRESSED
);
294 * F7 = "Decrease" key
295 * F8 = "Increase" key
297 if (keys
['c'] || keys
[SDLK_F8
]) {
299 } else if (keys
[SDLK_F7
]) {
304 play_sample_background(SOUND_RAIN
);
318 s
->night
= 1 - s
->night
;
320 if( s
->court_type
> GR_CTT_LAST
) {
321 s
->court_type
= GR_CTT_FIRST
;
322 } else if (s
->court_type
< GR_CTT_FIRST
) {
323 s
->court_type
= GR_CTT_LAST
;
327 if(!(SDL_GetTicks() < fading_start
+FADE_DURATION
) && !s
->is_over
) {
328 if( s
->player1
.type
== PLAYER_TYPE_HUMAN
) {
329 input_human( &s
->player1
,
330 keys
['w'] || keys
[SDLK_UP
] || s
->joystick_y
< -JOYSTICK_TRESHOLD
,
331 keys
['s'] || keys
[SDLK_DOWN
] || s
->joystick_y
> JOYSTICK_TRESHOLD
,
332 keys
['d'] || keys
[SDLK_SPACE
] || keys
[SDLK_LCTRL
] || keys
[SDLK_RETURN
] || s
->joystick_a
,
340 input_ai( &s
->player1
, &s
->ball
, &s
->player2
, s
);
343 if( s
->player2
.type
== PLAYER_TYPE_HUMAN
) {
344 input_human( &s
->player2
, keys
['o'], keys
['l'], keys
['k'], false, s
);
346 input_ai( &s
->player2
, &s
->ball
, &s
->player1
, s
);
350 /* Maemo: The "F6" button is the "Fullscreen" button */
351 if( keys
['f'] || keys
[SDLK_F6
]) SDL_WM_ToggleFullScreen( screen
);
352 if( keys
['y']) SDL_SaveBMP( screen
, "screenshot.bmp");
354 /* Maemo: The "F4" button is the "Open menu" button */
355 if( keys
['p'] || keys
[SDLK_F4
]) {
356 while( keys
['p'] || keys
[SDLK_F4
]) {
358 keys
= SDL_GetKeyState( NULL
);
361 while( (keys
['p'] || keys
[SDLK_F4
]) == 0) {
363 keys
= SDL_GetKeyState( NULL
);
366 while( keys
['p'] || keys
[SDLK_F4
]) {
368 keys
= SDL_GetKeyState( NULL
);
371 s
->was_stopped
= true;
374 if( keys
[SDLK_ESCAPE
] || keys
['q']) return true;
376 limit_value( &s
->player1
.y
, PLAYER_Y_MIN
, PLAYER_Y_MAX
);
377 limit_value( &s
->player2
.y
, PLAYER_Y_MIN
, PLAYER_Y_MAX
);
378 limit_value( &s
->ball
.jump
, BALL_JUMP_MIN
, BALL_JUMP_MAX
);
380 /* Update ball_dest for debugging purposes */
384 if (s
->ball
.move_x
> 0 && s
->wind
> 0) {
385 s
->ball
.x
+= fabsf(s
->wind
)/2;
386 } else if (s
->ball
.move_x
< 0 && s
->wind
< 0) {
387 s
->ball
.x
-= fabsf(s
->wind
)/2;
390 s
->ball
.x
+= s
->ball
.move_x
;
391 s
->ball
.y
+= s
->ball
.move_y
;
393 if(s
->player1
.state
) s
->player1
.state
--;
394 if(s
->player2
.state
) s
->player2
.state
--;
399 void render( GameState
* s
) {
401 #ifdef EXTENDED_REFEREE
404 if (s
->play_sound
!= SOUND_MAX
) {
405 play_sample(s
->play_sound
);
406 s
->play_sound
= SOUND_MAX
;
408 if( s
->winner
!= WINNER_NONE
) {
415 show_sprite( GR_RACKET
, 2*(s
->winner
-1), 4, WIDTH
/2 - get_image_width( GR_RACKET
)/8, HEIGHT
/2 - get_image_height( GR_RACKET
), 255);
416 sprintf( s
->game_score_str
, "player %d wins the match with %s", s
->winner
, format_sets( s
));
417 font_draw_string( GR_DKC2_FONT
, s
->game_score_str
, (WIDTH
-font_get_string_width( GR_DKC2_FONT
, s
->game_score_str
))/2, HEIGHT
/2 + 30, s
->time
/20, ANIMATION_WAVE
| ANIMATION_BUNGEE
);
421 if (s
->old_court_type
!= s
->court_type
|| s
->text_changed
|| s
->old_referee
!= s
->referee
) {
423 fill_image(s
->court_type
, 120, 120, 400, 250);
424 show_image(GR_COURT
, 0, 0, 255);
425 font_draw_string( GR_DKC2_FONT
, s
->game_score_str
, 14, 14, 0, ANIMATION_NONE
);
426 font_draw_string( GR_DKC2_FONT
, s
->sets_score_str
, (WIDTH
-font_get_string_width( GR_DKC2_FONT
, s
->sets_score_str
))-14, 14, 0, ANIMATION_NONE
);
427 #ifdef EXTENDED_REFEREE
428 switch (s
->referee
) {
435 case REFEREE_PLAYER1
:
436 case REFEREE_PLAYER2
:
455 show_sprite( GR_REFEREE
, s
->referee
*3+t
, 12, 250, 10, 255);
456 if (voice_finished_flag
== 0) {
457 show_sprite(GR_TALK
, (s
->time
/150)%2, 2, 280, 45, 255);
460 show_sprite( GR_REFEREE
, s
->referee
, 4, 250, 10, 255);
462 s
->old_court_type
= s
->court_type
;
463 s
->text_changed
= false;
464 s
->old_referee
= s
->referee
;
467 show_image( GR_SHADOW
, s
->ball
.x
-BALL_X_MID
, s
->ball
.y
+ get_phase( s
) - BALL_Y_MID
, 255);
469 show_sprite( GR_RACKET
, (!s
->player1
.state
), 4, s
->player1
.x
-RACKET_X_MID
, s
->player1
.y
-RACKET_Y_MID
, 255);
470 show_sprite( GR_RACKET
, (!s
->player2
.state
)+2, 4, s
->player2
.x
-RACKET_X_MID
, s
->player2
.y
-RACKET_Y_MID
, 255);
472 if( s
->ball
.move_x
> 0) {
473 show_sprite( GR_BALL
, (s
->time
/100)%BALL_STATES
, BALL_STATES
, s
->ball
.x
-BALL_X_MID
, s
->ball
.y
-BALL_Y_MID
, 255);
474 } else if( s
->ball
.move_x
< 0) {
475 show_sprite( GR_BALL
, BALL_STATES
-1-(s
->time
/100)%BALL_STATES
, BALL_STATES
, s
->ball
.x
-BALL_X_MID
, s
->ball
.y
-BALL_Y_MID
, 255);
477 show_sprite( GR_BALL
, 0, BALL_STATES
, s
->ball
.x
-BALL_X_MID
, s
->ball
.y
-BALL_Y_MID
, 255);
480 /* Player 1's mouse rectangle */
481 if (!(s
->player1
.mouse_locked
)) {
482 rectangle(s
->player1
.x
-2+5, s
->player1
.mouse_y
-2, 4, 4, 255, 255, 255);
483 rectangle(s
->player1
.x
-1+5, s
->player1
.mouse_y
-1, 2, 2, 0, 0, 0);
487 font_draw_string( GR_DKC2_FONT
, s
->status
, (WIDTH
-font_get_string_width( GR_DKC2_FONT
, s
->status
))/2, HEIGHT
-50, s
->time
/30, ANIMATION_WAVE
);
489 for (i
=0; i
<s
->rain
; i
++) {
492 draw_line_faded(x
, y
, x
+10+s
->wind
*5, y
+30, 0, 0, 255, 100, 200, 255);
496 * Cheap-ish update of the whole screen. This can
497 * probably be optimized.
499 update_rect(0, 0, WIDTH
, HEIGHT
);
503 line_horiz( s
->player1
.y
, 255, 0, 0);
504 line_horiz( s
->player2
.y
, 0, 0, 255);
505 line_horiz( s
->ball
.y
, 0, 255, 0);
507 line_vert( s
->player1
.x
, 255, 0, 0);
508 line_vert( s
->player2
.x
, 0, 0, 255);
509 line_vert( s
->ball
.x
, 0, 255, 0);
511 line_horiz( s
->player1
.ball_dest
, 255, 0, 255);
512 line_horiz( s
->player2
.ball_dest
, 0, 255, 255);
514 line_horiz( GAME_Y_MIN
, 100, 100, 100);
515 line_horiz( GAME_Y_MAX
, 100, 100, 100);
520 fill_image_offset(GR_FOG2
, 0, 0, WIDTH
, HEIGHT
, -s
->time
/150-s
->windtime
/200, 0);
522 fill_image_offset(GR_FOG
, 0, 0, WIDTH
, HEIGHT
, -s
->time
/100-s
->windtime
/150, 20);
524 fill_image_offset(GR_FOG2
, 0, 0, WIDTH
, HEIGHT
, -s
->time
/180-s
->windtime
/180, 80);
526 fill_image_offset(GR_FOG
, 0, 0, WIDTH
, HEIGHT
, s
->time
/200-s
->windtime
/100, 0);
531 show_image(GR_NIGHT
, 0, 0, 255);
537 void limit_value( float* value
, float min
, float max
) {
540 } else if( *value
> max
) {
545 float get_phase( GameState
* s
) {
547 float x
, min
, max
, direction
;
552 direction
= s
->ball
.move_x
;
554 pos
= (direction
>0)?(1-GROUND_PHASE
):(GROUND_PHASE
);
556 fract
= (x
-min
)/(max
-min
);
560 return fabsf( cosf(PI
*fract
/2))*PHASE_AMP
*s
->ball
.jump
;
562 fract
= (pos
-fract
)/(1-pos
);
563 return fabsf( sinf(PI
*fract
/2))*PHASE_AMP
*s
->ball
.jump
;
567 float get_move_y( GameState
* s
, unsigned char player
) {
568 float pct
, dest
, x_len
, y_len
;
569 float py
, by
, pa
, move_x
;
571 py
= (player
==1)?(s
->player1
.y
):(s
->player2
.y
);
574 move_x
= s
->ball
.move_x
;
576 /* -1.0 .. 1.0 for racket hit position */
577 pct
= (by
-py
)/(pa
/2);
578 limit_value( &pct
, -1.0, 1.0);
580 /* Y destination for ball */
581 dest
= GAME_Y_MID
+ pct
*(GAME_Y_MAX
-GAME_Y_MIN
);
583 s
->player1
.ball_dest
= dest
;
585 s
->player2
.ball_dest
= dest
;
588 /* lengths for the ball's journey */
590 x_len
= fabsf(GAME_X_MAX
- s
->ball
.x
);
591 y_len
= dest
- by
+ MOVE_Y_SEED
-rand()%MOVE_Y_SEED
*2;
593 x_len
= s
->ball
.x
- GAME_X_MIN
;
594 y_len
= by
- dest
+ MOVE_Y_SEED
-rand()%MOVE_Y_SEED
*2;
597 /* return the should-be value for move_y */
598 return (y_len
*move_x
)/(x_len
);
601 void input_human( Player
* player
, bool up
, bool down
, bool hit
, bool use_mouse
, GameState
* s
) {
602 int diff
= PLAYER_MOVE_Y
;
606 * Only use mouse control if the user isn't pressing any buttons
607 * this way, keyboard control still works when mouse control is
610 if (use_mouse
&& (down
|| up
|| hit
)) {
612 * this is here so if the user decides to play
613 * with keyboard controls, we will lock the
614 * mouse and disable displaying the mouse cursor
616 player
->mouse_locked
= true;
618 if (use_mouse
&& !down
&& !up
&& !hit
) {
619 mb
= SDL_GetMouseState(&(player
->mouse_x
), &(player
->mouse_y
));
620 if (mb
&SDL_BUTTON(SDL_BUTTON_LEFT
)) {
621 if (player
->mouse_y
< player
->y
) {
624 diff
= (player
->y
-player
->mouse_y
<diff
)?(player
->y
-player
->mouse_y
):(diff
);
625 } else if (player
->mouse_y
> player
->y
) {
628 diff
= (player
->mouse_y
-player
->y
<diff
)?(player
->mouse_y
-player
->y
):(diff
);
630 player
->mouse_locked
= false;
631 } else if (!player
->mouse_locked
) {
636 if (fabsf(s
->joystick_y
) > JOYSTICK_TRESHOLD
) {
637 diff
= PLAYER_MOVE_Y
*fabsf(s
->joystick_y
)/40.0;
638 if (diff
> PLAYER_MOVE_Y
) {
639 diff
= PLAYER_MOVE_Y
;
644 player
->y
-= fminf(diff
, diff
*player
->accelerate
);
645 player
->accelerate
*= PLAYER_ACCEL_INCREASE
;
647 player
->y
+= fminf(diff
, diff
*player
->accelerate
);
648 player
->accelerate
*= PLAYER_ACCEL_INCREASE
;
650 player
->accelerate
= PLAYER_ACCEL_DEFAULT
;
654 if( !player
->state
&& !player
->state_locked
) {
655 player
->state
= PLAYER_STATE_MAX
;
656 player
->state_locked
= true;
659 player
->state_locked
= false;
663 void input_ai( Player
* player
, Ball
* ball
, Player
* opponent
, GameState
* s
) {
667 if( fabsf( player
->y
- ball
->y
) > RACKET_Y_MID
*5) {
671 target
= GAME_Y_MID
+ (opponent
->ball_dest
- GAME_Y_MID
)/5;
673 if( player
->responsible
) {
674 if( player
->desire
== DESIRE_NORMAL
&& !IS_NEAR_Y_AI( player
->y
, ball
->y
)) {
675 if( player
->y
< ball
->y
) {
676 player
->y
+= fmin( 2*fact
, ball
->y
- player
->y
);
677 } else if( player
->y
> ball
->y
) {
678 player
->y
-= fmin( 2*fact
, player
->y
- ball
->y
);
682 if( (ball
->move_x
!= 0 || IS_NEAR_Y_AI( player
->y
, ball
->y
)) && IS_NEAR_X_AI( player
->x
, ball
->x
) && !player
->state
&& rand()%4==0) {
683 player
->state
= PLAYER_STATE_MAX
;
685 } else if( ball
->move_x
== 0) {
686 if( player
->desire
== DESIRE_NORMAL
&& !IS_NEAR_Y_AI( player
->y
, target
)) {
687 if( player
->y
< target
) {
688 player
->y
+= fmin( fact
, (target
-player
->y
)/40.0);
689 } else if( player
->y
> target
) {
690 player
->y
-= fmin( fact
, (player
->y
-target
)/40.0);
693 } else if( s
->ngram_prediction
> 0.0) {
694 target
= s
->ngram_prediction
*((float)HEIGHT
)/((float)(NGRAM_STEPS
));
695 target
= GAME_Y_MID
+ (target
-GAME_Y_MID
)*1.5;
697 if( player
->desire
== DESIRE_NORMAL
&& !IS_NEAR_Y_AI( player
->y
, target
)) {
698 if( player
->y
< target
) {
699 player
->y
+= fmin( fact
, (target
-player
->y
)/40.0);
700 } else if( player
->y
> target
) {
701 player
->y
-= fmin( fact
, (player
->y
-target
)/40.0);
705 if( player->desire == DESIRE_NORMAL) {
706 if( !IS_NEAR_Y_AI( player->y, target)) {
707 player->y += (target - player->y)/40.0;
713 void game_setup_serve( GameState
* s
) {
715 s
->ball
.y
= GAME_Y_MID
;
716 s
->ball
.move_x
= 0.0;
717 s
->ball
.move_y
= 0.0;
719 if( s
->player1_serves
) {
720 s
->player1
.responsible
= true;
721 s
->player1
.ball_dest
= 0.0;
722 s
->ball
.x
= GAME_X_MIN
-RACKET_X_MID
*1.5;
724 s
->player1
.responsible
= false;
725 s
->player2
.ball_dest
= 0.0;
726 s
->ball
.x
= GAME_X_MAX
+RACKET_X_MID
*1.5;
729 s
->player2
.responsible
= !(s
->player1
.responsible
);
732 float ngram_predictor( GameState
* s
) {
733 unsigned int count
= 0;
734 unsigned long sum
= 0;
738 if( s
->history_size
< 3) {
745 for( z
= 0; z
<NGRAM_STEPS
; z
++) {
746 count
+= s
->ngram
[x
][y
][z
];
747 sum
+= z
* s
->ngram
[x
][y
][z
];
750 result
= ((float)(sum
))/((float)(count
));
752 printf( "predicting next = %.2f\n", result
);
758 void score_game( GameState
* s
, bool player1_scored
) {
759 Player
* winner
= (player1_scored
)?(&(s
->player1
)):(&(s
->player2
));
760 Player
* loser
= (player1_scored
)?(&(s
->player2
)):(&(s
->player1
));
762 if( s
->current_set
>= SETS_TO_WIN
*2-1) {
767 if( loser
->game
< winner
->game
-1) {
768 if( winner
->game
>= 4) {
769 winner
->game
= loser
->game
= 0;
770 winner
->sets
[s
->current_set
]++;
772 #ifdef HAVE_VOICE_FILES
773 /* speak the current score */
774 voice_say_list(4, VOICE_ZERO_IN
+ (s
->player1
.sets
[s
->current_set
])*2, VOICE_TO
, VOICE_ZERO_OUT
+ (s
->player2
.sets
[s
->current_set
])*2, VOICE_IN_THE_FIRST_SET
+s
->current_set
);
777 /* scoring the set.. */
778 if( (winner
->sets
[s
->current_set
] == 6 && loser
->sets
[s
->current_set
] < 5) ||
779 winner
->sets
[s
->current_set
] == 7) {
781 s
->winner
= game_get_winner( s
);
787 char* format_sets( GameState
* s
) {
788 static char sets
[100];
789 static char tmp
[100];
790 int i
, max
= s
->current_set
;
794 if( s
->winner
!= WINNER_NONE
) {
797 for( i
=0; i
<=max
; i
++) {
798 sprintf( tmp
, "%d:%d, ", s
->player1
.sets
[i
], s
->player2
.sets
[i
]);
802 sets
[strlen(sets
)-2] = '\0';
807 char* format_game( GameState
* s
) {
808 static char game
[100];
809 static const int game_scoring
[] = { 0, 15, 30, 40 };
811 if( s
->player1
.game
< 4 && s
->player2
.game
< 4) {
812 #ifdef HAVE_VOICE_FILES
813 if (s
->player1
.game
> 0 || s
->player2
.game
> 0) {
814 if (s
->player1
.game
== s
->player2
.game
) {
815 voice_say_list(2, VOICE_LOVE_IN
+ 2*(s
->player1
.game
), VOICE_ALL
);
817 voice_say_list(2, VOICE_LOVE_IN
+ 2*(s
->player1
.game
), VOICE_LOVE_OUT
+ 2*(s
->player2
.game
));
821 sprintf( game
, "%d - %d", game_scoring
[s
->player1
.game
], game_scoring
[s
->player2
.game
]);
822 } else if( s
->player1
.game
> s
->player2
.game
) {
823 #ifdef HAVE_VOICE_FILES
824 voice_say_list(1, VOICE_ADVANTAGE_PLAYER_ONE
);
826 strcpy( game
, "advantage player 1");
827 } else if( s
->player1
.game
< s
->player2
.game
) {
828 #ifdef HAVE_VOICE_FILES
829 voice_say_list(1, VOICE_ADVANTAGE_PLAYER_TWO
);
831 strcpy( game
, "advantage player 2");
833 #ifdef HAVE_VOICE_FILES
834 voice_say_list(1, VOICE_DEUCE
);
836 strcpy( game
, "deuce");
842 char* format_status( GameState
* s
) {
843 static char status
[100];
844 static const char* set_names
[] = { "first", "second", "third", "fourth", "fifth" };
846 sprintf( status
, "%d:%d in %s set", s
->player1
.sets
[s
->current_set
], s
->player2
.sets
[s
->current_set
], set_names
[s
->current_set
]);
851 int game_get_winner( GameState
* s
) {
855 for( i
=0; i
<s
->current_set
; i
++) {
856 if( s
->player1
.sets
[i
] > s
->player2
.sets
[i
]) {
863 if( sets
[0] == SETS_TO_WIN
) return WINNER_PLAYER1
;
864 if( sets
[1] == SETS_TO_WIN
) return WINNER_PLAYER2
;