[gnome-games] Add annoying "bastard" mode



commit eb59c7464df3fe4e7538d2ce1be80946862a552b
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Sat Jun 6 12:01:16 2009 +0200

    Add annoying "bastard" mode
    
    Sometimes, gnometris is not evil enough.  This adds inpopular "Bastard" [1]
    mode to gnometris.  When enabled, it intentionally provides the user
    unsuitable pieces making the play a very frustrating experience.  It also
    annoys the user by displaying the most suitable piece instead of the next
    to come.
    
    In order to make it a bit fun, it is not that evil.  It more-or-less
    picks from three worst pieces giving the highest chance to the worst
    one so that the game is actually playable.
    
    The idea comes from "bastet" [2], which stands for "Bastard Tetris".
    This shares the algorithm, but no code.
    
    [1] http://games.slashdot.org/games/05/04/24/1259241.shtml?tid=208&tid=106
    [2] http://fph.altervista.org/prog/bastet.shtml
    
    Signed-off-by: Jason D. Clinton <me jasonclinton com>

 gnometris/blockops.cpp         |  141 ++++++++++++++++++++++++++++++++++++++--
 gnometris/blockops.h           |    5 ++
 gnometris/help/C/gnometris.xml |    6 ++-
 gnometris/tetris.cpp           |   20 ++++++
 gnometris/tetris.h             |    4 +
 5 files changed, 169 insertions(+), 7 deletions(-)
---
diff --git a/gnometris/blockops.cpp b/gnometris/blockops.cpp
index 071f332..b2b8abf 100644
--- a/gnometris/blockops.cpp
+++ b/gnometris/blockops.cpp
@@ -2,6 +2,7 @@
 /*
  * written by J. Marcin Gorycki <marcin gorycki intel com>
  * massively altered for Clutter by Jason D. Clinton <me jasonclinton com>
+ * "bastard" mode by Lubomir Rintel <lkundrak v3 sk>
  *
  * 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
@@ -152,6 +153,7 @@ BlockOps::BlockOps() :
 	blocknr(0),
 	rot(0),
 	color(0),
+	animate(true),
 	backgroundImage(NULL),
 	center_anchor_x(0),
 	center_anchor_y(0),
@@ -200,9 +202,11 @@ BlockOps::BlockOps() :
 								 NULL, 0);
 
 	field = new Block*[COLUMNS];
+	backfield = new Block*[COLUMNS];
 	for (int i = 0; i < COLUMNS; ++i)
 	{
 		field[i] = new Block[LINES];
+		backfield[i] = new Block[LINES];
 		for (int j = 0; j < LINES; ++j)
 		{
 			field[i][j].bindAnimations (this);
@@ -368,6 +372,8 @@ BlockOps::fallingToLaying()
 			if (field[x][y].what == FALLING)
 			{
 				field[x][y].what = LAYING;
+				if (!animate)
+					continue;
 				clutter_actor_set_position (field[x][y].actor,
 							    field[x][y].x,
 							    field[x][y].y);
@@ -453,20 +459,141 @@ BlockOps::checkFullLines()
 	return num_full_lines;
 }
 
+void
+BlockOps::saveField ()
+{
+	for (int y = 0; y < LINES; y++)
+		for (int x = 0; x < COLUMNS; x++)
+			backfield[x][y] = field[x][y];
+}
+
+void
+BlockOps::restoreField ()
+{
+	for (int y = 0; y < LINES; y++)
+		for (int x = 0; x < COLUMNS; x++)
+			field[x][y] = backfield[x][y];
+}
+
+/*
+ * An implementation of "Bastard" algorithm 
+ * it comes from Federico Poloni's "bastet"
+ */
+
+void
+BlockOps::bastardPick ()
+{
+	int scores[tableSize];
+	int blocks[tableSize];
+	int chance[tableSize];
+
+	animate = false;
+	/* This generates a priority for each block */
+	saveField ();
+	for (blocknr = 0; blocknr < tableSize; blocknr++)
+	{
+		scores[blocknr] = -32000;
+		for (rot = 0; rot < 4; rot++)
+		{
+			for (posx = 0; posx < COLUMNS; posx++)
+			{
+				int this_score = 0;
+				int x, y;
+
+				if (!blockOkHere(posx, posy = 0, blocknr, rot))
+					continue;
+
+				dropBlock();
+				fallingToLaying();
+
+				/* Count the completed lines */
+				for (y = MIN (posy + 4, LINES); y > 0; --y) {
+					if (checkFullLine(y)) {
+						this_score += 5000;
+					}
+				}
+
+				/* Count heights of columns */
+				for (x = 0; x < COLUMNS; x++)
+				{
+					for (y = 0; y < LINES; y++)
+						if (field[x][y].what == LAYING)
+							break;
+					this_score -= 5 * (LINES - y);
+				}
+
+				restoreField ();
+				if (scores[blocknr] < this_score)
+					scores[blocknr] = this_score;
+			}
+		}
+	}
+
+	for (int i = 0; i < tableSize; i++) {
+		/* Initialize chances table */
+		chance[i] = 100;
+		/* Initialize block/priority table */
+		blocks[i] = i;
+		/* Perturb score (-2 to +2), to avoid stupid tie handling */
+		scores[i] += g_random_int_range(-2, 2);
+	}
+
+	/* Sorts blocks by priorities, worst (interesting to us) first*/
+	for (int i = 0; i < tableSize; i++)
+	{
+		for (int ii = 0; ii < tableSize - 1; ii++)
+		{
+			if (scores[blocks[ii]] > scores[blocks[ii+1]])
+			{
+				int t = blocks[ii];
+				blocks[ii] = blocks[ii+1];
+				blocks[ii+1] = t;
+			}
+		}
+	}
+
+	/* Lower the chances we're giving the worst one */
+	chance[0] = 75;
+	chance[1] = 92;
+	chance[2] = 98;
+
+	/* Actually choose a piece */
+	int rnd = g_random_int_range(0, 99);
+	for (int i = 0; i < tableSize; i++)
+	{
+		blocknr = blocks[i];
+		if (rnd < chance[i])
+			break;
+	}
+
+	/* This will almost certainly not given next */
+	blocknr_next = blocks[tableSize-1];
+	animate = true;
+}
+
 bool
 BlockOps::generateFallingBlock()
 {
+	if (bastard_mode)
+	{
+		bastardPick();
+		color_next = -1;
+	}
+	else
+	{
+		blocknr = blocknr_next == -1 ? g_random_int_range(0, tableSize) :
+			blocknr_next;
+		blocknr_next = g_random_int_range(0, tableSize);
+	}
+
 	posx = COLUMNS / 2 + 1;
 	posy = 0;
 
-	blocknr = blocknr_next == -1 ? g_random_int_range(0, tableSize) :
-		blocknr_next;
 	rot = rot_next == -1 ? g_random_int_range(0, 4) : rot_next;
 	int cn = random_block_colors ? g_random_int_range(0, NCOLOURS) :
 		blocknr % NCOLOURS;
 	color = color_next == -1 ? cn : color_next;
 
-	blocknr_next = g_random_int_range(0, tableSize);
 	rot_next = g_random_int_range(0, 4);
 	color_next = random_block_colors ? g_random_int_range(0, NCOLOURS) :
 		blocknr_next % NCOLOURS;
@@ -553,7 +680,8 @@ BlockOps::moveBlockInField (gint x_trans, gint y_trans)
 				blocks_actor[x][y] = field[i-x_trans][j-y_trans].actor;
 				field[i-x_trans][j-y_trans].what = EMPTY;
 				field[i-x_trans][j-y_trans].actor = NULL;
-				clutter_behaviour_remove_all (field[i-x_trans][j-y_trans].move_behaviour);
+				if (animate)
+					clutter_behaviour_remove_all (field[i-x_trans][j-y_trans].move_behaviour);
 			}
 		}
 	}
@@ -566,7 +694,7 @@ BlockOps::moveBlockInField (gint x_trans, gint y_trans)
 				field[i][j].what = FALLING;
 				field[i][j].color = color;
 				field[i][j].actor = blocks_actor[x][y];
-				if (field[i][j].actor) {
+				if (field[i][j].actor && animate) {
 					gint cur_x, cur_y = 0;
 					g_object_get (G_OBJECT (field[i][j].actor), "x", &cur_x, "y", &cur_y, NULL);
 					ClutterPath *path_line = clutter_path_new ();
@@ -579,7 +707,8 @@ BlockOps::moveBlockInField (gint x_trans, gint y_trans)
 			}
 		}
 	}
-	clutter_timeline_start (move_time);
+	if (animate)
+		clutter_timeline_start (move_time);
 }
 
 bool
diff --git a/gnometris/blockops.h b/gnometris/blockops.h
index 7a2fd63..85909fe 100644
--- a/gnometris/blockops.h
+++ b/gnometris/blockops.h
@@ -119,6 +119,9 @@ private:
 	bool blockOkHere (int x, int y, int b, int r);
 	void eliminateLine (int l);
 	bool checkFullLine(int l);
+	void bastardPick ();
+	void saveField ();
+	void restoreField ();
 
 	GtkWidget * w;
 
@@ -132,6 +135,7 @@ private:
 	gint themeID;
 
 	Block **field;
+	Block **backfield;
 
 	int blocknr;
 	int rot;
@@ -139,6 +143,7 @@ private:
 
 	bool showPause;
 	bool showGameOver;
+	bool animate;
 
 	GdkPixbuf *backgroundImage;
 	bool backgroundImageTiled;
diff --git a/gnometris/help/C/gnometris.xml b/gnometris/help/C/gnometris.xml
index e315dd3..f739233 100644
--- a/gnometris/help/C/gnometris.xml
+++ b/gnometris/help/C/gnometris.xml
@@ -201,7 +201,7 @@ application or
      </sect3>
    </sect2>
    <sect2 id="s3-customblocks">
-    <title>Customizing the Blocks</title>
+    <title>Customizing the Gameplay</title>
     <para>From <menuchoice><guimenu>Settings</guimenu><guimenuitem>Preferences</guimenuitem></menuchoice>, you can customize a variety of features related to the blocks: previewing the next block, block colors, and block rotation.</para>
       <sect3 id="s3-sound">
 	<title>Enable sounds</title>
@@ -215,6 +215,10 @@ application or
       <title>Use Random Block Colors</title>
        <para>Choose if you want the various block configurations to be color coded or randomly colored. If you use the colors to help you identify blocks, random colors will make the game more difficult.</para>
      </sect3>
+     <sect3 id="s3-bastard-mode">
+      <title>Bastard Mode</title>
+       <para>Choose this option if you want to select mode that is designed specifically to annoy the player. With this feature enabled, you never get the piece you want, and very seldom complete a row. Next block preview doesn't correspond to actual piece you get next, but to the one you probably wish to get.</para>
+     </sect3>
      <sect3 id="s3-block-rotation">
       <title>Rotate Blocks Counterclockwise</title>
        <para>Choose this option if you want the blocks to rotate counterclockwise or not.</para>
diff --git a/gnometris/tetris.cpp b/gnometris/tetris.cpp
index f22997a..0cdd260 100644
--- a/gnometris/tetris.cpp
+++ b/gnometris/tetris.cpp
@@ -56,6 +56,7 @@ int rot_next = -1;
 int color_next = -1;
 
 bool random_block_colors = false;
+bool bastard_mode = false;
 bool do_preview = true;
 bool default_bgimage = false;
 bool rotateCounterClockWise = true;
@@ -430,6 +431,8 @@ Tetris::initOptions ()
 
 	random_block_colors = confGetBoolean (KEY_OPTIONS_GROUP, KEY_RANDOM_BLOCK_COLORS, TRUE);
 
+	bastard_mode = confGetBoolean (KEY_OPTIONS_GROUP, KEY_BASTARD_MODE, FALSE);
+
 	rotateCounterClockWise = confGetBoolean (KEY_OPTIONS_GROUP, KEY_ROTATE_COUNTER_CLOCKWISE, TRUE);
 
 	line_fill_height = confGetInt (KEY_OPTIONS_GROUP, KEY_LINE_FILL_HEIGHT, 0);
@@ -468,6 +471,7 @@ Tetris::setOptions ()
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sound_toggle), games_sound_is_enabled ());
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (do_preview_toggle), do_preview);
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (random_block_colors_toggle), random_block_colors);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bastard_mode_toggle), bastard_mode);
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rotate_counter_clock_wise_toggle), rotateCounterClockWise);
 
 		if (theme_preview) {
@@ -502,6 +506,13 @@ Tetris::setSelectionBlocks(GtkWidget *widget, void *d)
 }
 
 void
+Tetris::setBastardMode(GtkWidget *widget, void *d)
+{
+	games_conf_set_boolean (KEY_OPTIONS_GROUP, KEY_BASTARD_MODE,
+				GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+void
 Tetris::setRotateCounterClockWise(GtkWidget *widget, void *d)
 {
 	games_conf_set_boolean (KEY_OPTIONS_GROUP, KEY_ROTATE_COUNTER_CLOCKWISE,
@@ -683,6 +694,15 @@ Tetris::gameProperties(GtkAction *action, void *d)
 	gtk_box_pack_start (GTK_BOX (fvbox), t->random_block_colors_toggle,
 			    0, 0, 0);
 
+	/* bastard mode */
+	t->bastard_mode_toggle =
+		gtk_check_button_new_with_mnemonic (_("_Bastard mode"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (t->bastard_mode_toggle),
+				     bastard_mode);
+	g_signal_connect (t->bastard_mode_toggle, "clicked",
+			  G_CALLBACK (setBastardMode), d);
+	gtk_box_pack_start (GTK_BOX (fvbox), t->bastard_mode_toggle, 0, 0, 0);
+
 	/* rotate counter clock wise */
  	t->rotate_counter_clock_wise_toggle =
 		gtk_check_button_new_with_mnemonic (_("_Rotate blocks counterclockwise"));
diff --git a/gnometris/tetris.h b/gnometris/tetris.h
index f65ede8..6690a3d 100644
--- a/gnometris/tetris.h
+++ b/gnometris/tetris.h
@@ -36,6 +36,7 @@
 #define KEY_DO_PREVIEW                "do_preview"
 #define KEY_LINE_FILL_HEIGHT          "line_fill_height"
 #define KEY_LINE_FILL_PROBABILITY     "line_fill_probability"
+#define KEY_BASTARD_MODE              "bastard_mode"
 #define KEY_RANDOM_BLOCK_COLORS       "random_block_colors"
 #define KEY_ROTATE_COUNTER_CLOCKWISE  "rotate_counter_clock_wise"
 #define KEY_SOUND                     "sound"
@@ -64,6 +65,7 @@ extern int blocknr_next;
 extern int rot_next;
 
 extern bool random_block_colors;
+extern bool bastard_mode;
 
 class Preview;
 class BlockOps;
@@ -137,6 +139,7 @@ private:
 	static void setSound (GtkWidget * widget, gpointer data);
 	static void setSelectionPreview (GtkWidget * widget, void *d);
 	static void setSelectionBlocks (GtkWidget * widget, void *d);
+	static void setBastardMode (GtkWidget * widget, void *d);
 	static void setRotateCounterClockWise (GtkWidget * widget, void *d);
 	static void setTarget (GtkWidget * widget, void *d);
 	static void setSelection (GtkWidget * widget, void *data);
@@ -176,6 +179,7 @@ private:
 	GtkWidget *fill_prob_spinner;
 	GtkWidget *do_preview_toggle;
 	GtkWidget *random_block_colors_toggle;
+	GtkWidget *bastard_mode_toggle;
 	GtkWidget *rotate_counter_clock_wise_toggle;
 	GtkWidget *useTargetToggle;
 	GtkWidget *sound_toggle;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]