Files
commandergenius/project/jni/application/ltris-1.0.15/src/cpu.c
2010-12-24 12:04:27 +00:00

380 lines
13 KiB
C

/***************************************************************************
cpu.c - description
-------------------
begin : Sun Jan 6 2002
copyright : (C) 2001 by Michael Speck
email : kulkanie@gmx.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "ltris.h"
#include "list.h"
#include "cpu.h"
typedef struct {
int x, y, rot; /* destination data used by bowl */
int score; /* score of this destination. highest score wins */
/* debug score mods */
int holes_mod, complete_mod, alt_mod, steep_mod, abyss_mod, block_mod;
} CPU_Dest;
/*
====================================================================
Locals
====================================================================
*/
/*
====================================================================
Reset bowl with original data:
empty = 0
blocked = 1
====================================================================
*/
void cpu_reset_bowl( CPU_Data *cpu_data )
{
int i, j;
for ( i = 0; i < cpu_data->bowl_w; i++ )
for ( j = 0; j < cpu_data->bowl_h; j++ )
cpu_data->bowl[i][j] = cpu_data->original_bowl[i][j];
}
/*
====================================================================
Check if cpu_block fits in at this position.
====================================================================
*/
int cpu_validate_block_pos( CPU_Data *cpu_data, int x, int y, int rot )
{
int i, j;
for ( j = 3; j >= 0; j-- )
for ( i = 3; i >= 0; i-- )
if ( cpu_data->block->mask[rot][i][j] ) {
if ( x + i < 0 || x + i >= cpu_data->bowl_w )
return 0;
if ( y + j >= cpu_data->bowl_h )
return 0;
if ( y + j < 0 )
continue;
if ( cpu_data->bowl[x + i][y + j] )
return 0;
}
return 1;
}
/*
====================================================================
Check if the passed position is valid for cpu_data->block, drop the
block and insert to cpu_bowl with index 2.
====================================================================
*/
int cpu_insert_block( CPU_Data *cpu_data, int x, int rot, int *y )
{
int i, j;
/* check if out of bowl at the sides*/
for ( i = 0; i < 4; i++ ) {
for ( j = 0; j < 4; j++ )
if ( cpu_data->block->mask[rot][i][j] )
if ( x + i < 0 || x + i >= cpu_data->bowl_w )
return 0;
}
/* drop tile down */
*y = -3;
while ( cpu_validate_block_pos( cpu_data, x, *y + 1, rot ) )
(*y)++;
/* insert block */
for ( i = 0; i < 4; i++ ) {
for ( j = 0; j < 4; j++ )
if ( cpu_data->block->mask[rot][i][j] )
cpu_data->bowl[x + i][(*y) + j] = 2;
}
return 1;
}
/*
====================================================================
Count number of completed lines.
====================================================================
*/
int cpu_count_compl_lines( CPU_Data *cpu_data )
{
int i, j, line_count;
line_count = 0;
for ( j = 0; j < cpu_data->bowl_h; j++ ) {
for ( i = 0; i < cpu_data->bowl_w; i++ )
if ( !cpu_data->bowl[i][j] ) break;
if ( i == cpu_data->bowl_w ) {
for ( i = 0; i < cpu_data->bowl_w; i++ )
cpu_data->bowl[i][j] = 3;
line_count++;
}
}
return line_count;
}
/*
====================================================================
Remove completed lines.
====================================================================
*/
void cpu_remove_compl_lines( CPU_Data *cpu_data )
{
int i, j, l;
int line_count = 0;
int line_y[4];
/* count lines and get pos */
for ( j = 0; j < cpu_data->bowl_h; j++ ) {
if ( cpu_data->bowl[0][j] == 3 ) {
line_y[line_count++] = j;
}
}
/* remove */
for ( j = 0; j < line_count; j++ )
for ( i = 0; i < cpu_data->bowl_w; i++ ) {
for ( l = line_y[j]; l > 0; l-- )
cpu_data->bowl[i][l] = cpu_data->bowl[i][l - 1];
cpu_data->bowl[i][0] = 0;
}
}
/*
====================================================================
Get alteration of column.
====================================================================
*/
int cpu_get_alt( CPU_Data *cpu_data, int col )
{
int j;
if ( col == -1 || col == cpu_data->bowl_w )
return cpu_data->bowl_h;
for ( j = 0; j < cpu_data->bowl_h; j++ )
if ( cpu_data->bowl[col][j] != 0 )
break;
return cpu_data->bowl_h - j;
}
/*
====================================================================
This is the main analyze function.
CPU_Data::bowl has the block already added and 'dest' already
contains the position of the block and does only need the score.
====================================================================
*/
int CPU_SCORE_BASIC = 100; /* starting score */
int CPU_SCORE_HOLE = -26; /* each single hole */
int CPU_SCORE_ALT = 5; /* each altitude level (tile ) */
int CPU_SCORE_LINE = 17; /* each line completed */
int CPU_SCORE_STEEP = -3; /* steepness score */
int CPU_SCORE_ABYSS = -7; /* an abyss hole counts this score */
int CPU_SCORE_BLOCK = -5; /* every tile above the last hole belonging to the inserted block cashes this penalty */
void cpu_analyze_bowl( CPU_Data *cpu_data, CPU_Dest *dest )
{
int i, j;
int line_count;
int max_alt = 0;
int line_score;
int y, abyss_depth;
int diff;
int aux_alt;
/* count completed lines -- lines that will be removed are marked as 3 */
line_count = cpu_count_compl_lines( cpu_data );
cpu_remove_compl_lines( cpu_data );
/* get maximum alt */
for ( i = 0; i < cpu_data->bowl_w; i++ ) {
if ( max_alt < cpu_get_alt( cpu_data, i ) )
max_alt = cpu_get_alt( cpu_data, i );
}
/* ANALYZE */
/* LINES */
/* clearing a single line is quite bad as there'll propably be holes and
* some tiles remaining so we don't give any bonus for this except if
* the bowl contents grows to high. legalize it!
*/
line_score = CPU_SCORE_LINE + (max_alt/2);
if ( line_count == 1 ) {
switch ( cpu_data->aggr ) {
case 0:
/* defensive */
if ( max_alt > 9 )
dest->complete_mod = line_score;
break;
case 1:
/* normal */
if ( max_alt <= 5 )
dest->complete_mod = -line_score;
else
if ( max_alt <= 12 )
dest->complete_mod = -(line_score/2);
break;
case 2:
/* aggressive */
if ( max_alt <= 12 )
dest->complete_mod = -line_score;
break;
case 3:
/* kamikaze */
if ( max_alt <= 8 )
dest->complete_mod = -(line_score * 2);
else
if ( max_alt <= 14 )
dest->complete_mod = -line_score;
break;
}
}
else
dest->complete_mod = line_score * line_count * line_count;
/* HOLES */
/* each hole simply count a score */
for ( i = 0; i < cpu_data->bowl_w; i++ ) {
for ( j = cpu_data->bowl_h - 1; j > cpu_data->bowl_h - 1 - cpu_get_alt( cpu_data, i ); j-- ) {
if ( !cpu_data->bowl[i][j] )
dest->holes_mod += CPU_SCORE_HOLE;
}
}
/* ALTITUDE */
/* the deeper you place the tile the better */
dest->alt_mod = CPU_SCORE_ALT * dest->y;
/* STEEPNESS */
/* height difference to the neighbored tiles */
for ( i = 1; i < BOWL_WIDTH - 1; i++ ) {
diff = abs( cpu_get_alt( cpu_data, i ) - cpu_get_alt( cpu_data, i - 1 ) ) + abs( cpu_get_alt( cpu_data, i ) - cpu_get_alt( cpu_data, i + 1 ) );
dest->steep_mod += CPU_SCORE_STEEP;
}
/* ABYSS */
/* abyss is the deepness of a struct with a single hole at the highest point */
for ( i = 0; i < cpu_data->bowl_w; i++ ) {
/* get deepest point */
aux_alt = cpu_get_alt( cpu_data, i - 1 );
if ( cpu_get_alt( cpu_data, i + 1 ) < aux_alt )
aux_alt = cpu_get_alt( cpu_data, i + 1 );
/* if this value is below the col's alt we don't have an abyss */
if ( cpu_get_alt( cpu_data, i ) >= aux_alt )
continue;
y = cpu_data->bowl_h - aux_alt - 1;
/* check abyss_depth */
abyss_depth = 0;
while ( y + 1 < cpu_data->bowl_h && !cpu_data->bowl[i][y + 1] ) {
abyss_depth++;
y++;
}
/* we're at position above the abyss and have it's height */
if ( abyss_depth >= 2 )
dest->abyss_mod += CPU_SCORE_ABYSS * abyss_depth;
}
/* BLOCK */
/* to keep the bowl down we need to complete lines and therefore keep the upper holes in reach.
* therefore we punish each tile above the last hole. as already inserted blocks doesn't matter
* we just score the current block. */
for ( i = 0; i < cpu_data->bowl_w; i++ ) {
/* first col tile */
y = cpu_data->bowl_h - cpu_get_alt( cpu_data, i );
while ( y < cpu_data->bowl_h && cpu_data->bowl[i][y] ) {
if ( cpu_data->bowl[i][y] == 2 )
dest->block_mod += CPU_SCORE_BLOCK;
y++;
}
}
/* score together */
dest->score = CPU_SCORE_BASIC + dest->holes_mod + dest->complete_mod +
dest->alt_mod + dest->abyss_mod + dest->steep_mod + dest->block_mod;
}
/*
====================================================================
Backup/restore bowl information. Used for preview analyzes.
====================================================================
*/
void cpu_backup_bowl( CPU_Data *cpu_data )
{
int i, j;
for ( i = 0; i < cpu_data->bowl_w; i++ )
for ( j = 0; j < cpu_data->bowl_h; j++ )
cpu_data->backup_bowl[i][j] = cpu_data->bowl[i][j];
}
void cpu_restore_bowl( CPU_Data *cpu_data )
{
int i, j;
for ( i = 0; i < cpu_data->bowl_w; i++ )
for ( j = 0; j < cpu_data->bowl_h; j++ )
cpu_data->bowl[i][j] = cpu_data->backup_bowl[i][j];
}
/*
====================================================================
Publics
====================================================================
*/
/*
====================================================================
Analyze situation and set CPU_Data::dest_x and CPU_Data::dest_rot
which is used by bowl to move the block.
The important part is the cpu_analyze_bowl() function above.
You want your own AI... edit this function!.
====================================================================
*/
void cpu_analyze_data( CPU_Data *cpu_data )
{
int x, rot, y;
CPU_Dest *dest, *best_dest;
List *list = list_create( LIST_AUTO_DELETE, LIST_NO_CALLBACK );
/* get and analyze valid positions of block */
cpu_data->block = cpu_data->original_block;
for ( rot = 0; rot < 4; rot++ ) {
for ( x = -4; x < 14; x++ ) {
cpu_reset_bowl( cpu_data );
if ( cpu_insert_block( cpu_data, x, rot, &y ) ) {
dest = calloc( 1, sizeof( CPU_Dest ) );
dest->x = x;
dest->y = y;
dest->rot = rot;
cpu_analyze_bowl( cpu_data, dest );
list_add( list, dest );
}
}
}
/* get highest score as final dest -- same score the deeper block is used */
best_dest = list_first( list );
while ( ( dest = list_next( list ) ) != 0 ) {
if ( dest->score > best_dest->score )
best_dest = dest;
else
if ( dest->score == best_dest->score && dest->y > best_dest->y )
best_dest = dest;
}
cpu_data->dest_x = best_dest->x;
cpu_data->dest_y = best_dest->y;
cpu_data->dest_rot = best_dest->rot;
cpu_data->dest_score = best_dest->score;
/* DEBUG */
/* printf( "DESTINATION: %i/%i\n", best_dest->x, best_dest->rot);
printf( "Lines: %6i\n", best_dest->complete_mod );
printf( "Alt: %6i\n", best_dest->alt_mod );
printf( "Holes: %6i\n", best_dest->holes_mod );
printf( "Steepness: %6i\n", best_dest->steep_mod );
printf( "Abyss: %6i\n", best_dest->abyss_mod );
printf( "Blocking: %6i\n", best_dest->block_mod );
printf( "---------- %6i\n", best_dest->score );*/
list_delete( list );
}