Universe generator: resource providers

* The universe generator has been modified to generate resource
providers. The code attempts to keep the universe balanced according to
the natural resources definitions.
This commit is contained in:
Emmanuel BENOîT 2012-01-06 10:05:47 +01:00
parent e7d2072813
commit b054a379a9
24 changed files with 1105 additions and 6 deletions

View file

@ -0,0 +1,44 @@
/*
* Test the verse.get_random_part() function
*/
BEGIN;
SELECT plan( 6 );
/* First set of tests: results of the function with a fake
* verse.random_deviation() that always returns the minimal possible
* value.
*/
CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
RETURNS DOUBLE PRECISION
STRICT VOLATILE
AS $$
SELECT $1 - $2;
$$ LANGUAGE SQL;
SELECT diag_test_name( 'verse.get_random_part() - Single part, minimal random value' );
SELECT is( verse.get_random_part( 0.5 , 1 , 0.5 , 0.25 ) , 0.5::DOUBLE PRECISION );
SELECT diag_test_name( 'verse.get_random_part() - Two parts, some extra quantity, minimal random value' );
SELECT is( verse.get_random_part( 0.75 , 2 , 0.5 , 0.25 ) , 0.25::DOUBLE PRECISION );
SELECT diag_test_name( 'verse.get_random_part() - Two parts, maximal quantity, minimal random value' );
SELECT is( verse.get_random_part( 1.5 , 2 , 0.5 , 0.25 ) , 0.75::DOUBLE PRECISION );
/* Second set of tests: results of the function with a fake
* verse.random_deviation() that always returns the maximal possible
* value.
*/
CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
RETURNS DOUBLE PRECISION
STRICT VOLATILE
AS $$
SELECT $1 + $2;
$$ LANGUAGE SQL;
SELECT diag_test_name( 'verse.get_random_part() - Single part, maximal random value' );
SELECT is( verse.get_random_part( 0.5 , 1 , 0.5 , 0.25 ) , 0.5::DOUBLE PRECISION );
SELECT diag_test_name( 'verse.get_random_part() - Two parts, some extra quantity, maximal random value' );
SELECT is( verse.get_random_part( 1 , 2 , 0.5 , 0.25 ) , 0.75::DOUBLE PRECISION );
SELECT diag_test_name( 'verse.get_random_part() - Two parts, minimal quantity, maximal random value' );
SELECT is( verse.get_random_part( 0.5 , 2 , 0.5 , 0.25 ) , 0.25::DOUBLE PRECISION );
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,41 @@
/*
* Test the verse.list_random_planets_in() function
*/
BEGIN;
/* We need two systems with one planet each. Systems will be located at
* (0,0) and (1,1).
*/
\i utils/strings.sql
\i utils/accounts.sql
\i utils/naming.sql
\i utils/universe.sql
SELECT _create_raw_planets( 10 , 'test' );
/***** TESTS BEGIN HERE *****/
SELECT plan( 5 );
SELECT diag_test_name( 'verse.list_random_planets_in() - Empty set if count = 0' );
SELECT is_empty( $$
SELECT * FROM verse.list_random_planets_in( ROW( 0 , 0 , 1 , 1 ) , 0 )
$$ );
SELECT diag_test_name( 'verse.list_random_planets_in() - Empty set if outside of existing universe' );
SELECT is_empty( $$
SELECT * FROM verse.list_random_planets_in( ROW( 2 , 2 , 3 , 3 ) , 2 )
$$ );
SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count > actual count' );
SELECT is( COUNT(*) , 5::BIGINT )
FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 7 );
SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count = actual count' );
SELECT is( COUNT(*) , 5::BIGINT )
FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 5 );
SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count < actual count' );
SELECT is( COUNT(*) , 4::BIGINT )
FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 4 );
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,72 @@
/*
* Test the verse.collect_resprov_statistics( ) function
*/
BEGIN;
\i utils/strings.sql
\i utils/resources.sql
\i utils/accounts.sql
\i utils/naming.sql
\i utils/universe.sql
SELECT _create_natural_resources( 2 , 'testResource' );
SELECT _create_raw_planets( 5 , 'testPlanet' );
/****** TESTS BEGIN HERE ******/
SELECT plan( 6 );
SELECT verse.collect_resprov_statistics( );
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - Temporary table creation' );
SELECT has_table( 'rp_stats' );
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - Temporary table rows' );
SELECT is( COUNT(*)::INT , 2 ) FROM rp_stats;
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats contains definition data' );
SELECT is( COUNT(*)::INT , 2 )
FROM (
SELECT ( d.natres_quantity_avg = s.quantity_avg
AND d.natres_quantity_dev = s.quantity_dev
AND d.natres_difficulty_avg = s.difficulty_avg
AND d.natres_difficulty_dev = s.difficulty_dev
AND d.natres_recovery_avg = s.recovery_avg
AND d.natres_recovery_dev = s.recovery_dev
AND d.natres_p_presence = s.presence ) AS ok
FROM rp_stats s
INNER JOIN defs.natural_resources d
USING ( resource_name_id )
) sq
WHERE sq.ok;
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats contains planet counts' );
SELECT is( COUNT(*)::INT , 2 )
FROM (
SELECT planets = 5 AS ok
FROM rp_stats
) sq
WHERE sq.ok;
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats sums at 0 with no providers' );
SELECT is( COUNT(*)::INT , 2 )
FROM (
SELECT ( providers = 0 AND quantity = 0
AND difficulty = 0 AND recovery = 0 ) AS ok
FROM rp_stats
) sq
WHERE sq.ok;
DROP TABLE rp_stats;
SELECT _create_resource_provider( 'testPlanet1' , 'testResource1' );
SELECT verse.collect_resprov_statistics( );
SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats sums' );
SELECT is( COUNT(*)::INT , 1 )
FROM (
SELECT ( resource_name_id = _get_string( 'testResource1' )
AND providers = 1 AND quantity = 100
AND difficulty = 0.5 AND recovery = 0.5 ) AS ok
FROM rp_stats
) sq
WHERE sq.ok;
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,56 @@
/*
* Test the verse.compute_rpp_delta( ) function
*/
BEGIN;
SELECT plan( 12 );
/* First set of tests: results of the function with a fake
* verse.random_deviation() that always returns the minimal possible
* value.
*/
CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
RETURNS DOUBLE PRECISION
STRICT VOLATILE
AS $$
SELECT $1 - $2;
$$ LANGUAGE SQL;
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - No existing value, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 0 , 10 , 0 , 1 , 0.5 )::NUMERIC , 5.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value below minimum, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 0 , 1 , 0.5 )::NUMERIC , 10.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at minimum, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 5 , 1 , 0.5 )::NUMERIC , 5.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at average, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 10 , 1 , 0.5 )::NUMERIC , 5.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at maximum, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 15 , 1 , 0.5 )::NUMERIC , 5.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value over maximum, random at minimal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 20 , 1 , 0.5 )::NUMERIC , 5.0 );
/* Second set of tests: results of the function with a fake
* verse.random_deviation() that always returns the maximal possible
* value.
*/
CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
RETURNS DOUBLE PRECISION
STRICT VOLATILE
AS $$
SELECT $1 + $2;
$$ LANGUAGE SQL;
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - No existing value, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 0 , 10 , 0 , 1 , 0.5 )::NUMERIC , 15.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value below minimum, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 0 , 1 , 0.5 )::NUMERIC , 15.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at minimum, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 5 , 1 , 0.5 )::NUMERIC , 15.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at average, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 10 , 1 , 0.5 )::NUMERIC , 15.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at maximum, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 15 , 1 , 0.5 )::NUMERIC , 15.0 );
SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value over maximum, random at maximal value' );
SELECT is( verse.compute_rpp_delta( 10 , 10 , 20 , 1 , 0.5 )::NUMERIC , 10.0 );
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,62 @@
/*
* Test the verse.create_resource_provider( ) function
*/
BEGIN;
/* Before any actual testing, we need to drop FK constraints on the RP
* table and create a table which will contain the results of the
* function.
*/
ALTER TABLE verse.resource_providers
DROP CONSTRAINT fk_resprov_planet ,
DROP CONSTRAINT fk_resprov_resource;
CREATE TEMPORARY TABLE test_result(
_providers_left INT ,
_tot_quantity DOUBLE PRECISION ,
_tot_difficulty DOUBLE PRECISION ,
_tot_recovery DOUBLE PRECISION
) ON COMMIT DROP;
/* Now we call the function using a crafted resource statistics row which
* will make it easy to check the results.
*/
INSERT INTO test_result
SELECT * FROM verse.create_resource_provider( 12 ,
ROW( 34 , 0.0 , 0.0 , 0.0 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 ) ,
2 , 10 , 1.5 , 1 );
/***** TESTS BEGIN HERE *****/
SELECT plan( 11 );
SELECT diag_test_name( 'verse.create_resource_provider( ) - Output exists' );
SELECT is( COUNT(*)::INT , 1 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( ) - _providers_left updated' );
SELECT is( _providers_left , 1 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_quantity updated' );
SELECT is( _tot_quantity::NUMERIC , 5.0 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_difficulty updated' );
SELECT is( _tot_difficulty::NUMERIC , 0.75 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_recovery updated' );
SELECT is( _tot_recovery::NUMERIC , 0.5 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider exists' );
SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider primary key' );
SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers
WHERE planet_id = 12 AND resource_name_id = 34;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider is full' );
SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers
WHERE resprov_quantity = resprov_quantity_max;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider maximal quantity' );
SELECT is( resprov_quantity_max::NUMERIC , 5.0 ) FROM verse.resource_providers;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider extraction difficulty' );
SELECT is( resprov_difficulty::NUMERIC , 0.75 ) FROM verse.resource_providers;
SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider recovery rate' );
SELECT is( resprov_recovery::NUMERIC , 0.5 ) FROM verse.resource_providers;
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,129 @@
/*
* Check the variant of verse.create_resource_providers( ) which takes a
* resource statistics record as a parameter.
*/
BEGIN;
/* Temporary table which will store information about calls to the
* verse.create_resource_provider( ) function
*/
CREATE TEMPORARY TABLE test_result(
planet INT ,
resource INT ,
quantity DOUBLE PRECISION ,
difficulty DOUBLE PRECISION ,
recovery DOUBLE PRECISION
) ON COMMIT DROP;
/* Replace verse.list_random_planets_in() with a function that simply
* counts.
*/
CREATE OR REPLACE FUNCTION verse.list_random_planets_in(
_area verse.generator_area_type ,
_count INT )
RETURNS SETOF INT
STRICT VOLATILE
SECURITY INVOKER
AS $list_random_planets_in$
DECLARE
i INT;
BEGIN
FOR i IN 1 .. _count
LOOP
RETURN NEXT i;
END LOOP;
END;
$list_random_planets_in$ LANGUAGE PLPGSQL;
/* Replace verse.create_resource_provider( ) with a function that writes
* to the test_result temporary table.
*/
CREATE OR REPLACE FUNCTION verse.create_resource_provider(
_planet INT ,
_data verse.resprov_generator_type ,
INOUT _providers_left INT ,
INOUT _tot_quantity DOUBLE PRECISION ,
INOUT _tot_difficulty DOUBLE PRECISION ,
INOUT _tot_recovery DOUBLE PRECISION )
STRICT VOLATILE
SECURITY INVOKER
AS $create_resource_provider$
BEGIN
INSERT INTO test_result VALUES(
_planet , _data.resource_name_id ,
_tot_quantity / _providers_left::DOUBLE PRECISION ,
_tot_difficulty / _providers_left::DOUBLE PRECISION ,
_tot_recovery / _providers_left::DOUBLE PRECISION
);
_tot_quantity := ( _providers_left - 1 ) * _tot_quantity / _providers_left;
_tot_difficulty := ( _providers_left - 1 ) * _tot_difficulty / _providers_left;
_tot_recovery := ( _providers_left - 1 ) * _tot_recovery / _providers_left;
_providers_left := _providers_left - 1;
END;
$create_resource_provider$ LANGUAGE PLPGSQL;
/***** TESTS BEGIN HERE *****/
SELECT plan( 11 );
/* First set of test: empty universe */
SELECT verse.create_resource_providers(
ROW( 0 , 0 , 1 , 1 ) ,
ROW( 42 , 0.0 , 0.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
);
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Providers created' );
SELECT is( COUNT(*)::INT , 10 ) FROM test_result WHERE resource = 42;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Planet identifiers' );
SELECT results_eq(
'SELECT planet FROM test_result ORDER BY planet' ,
ARRAY[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
);
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total quantity' );
SELECT is( SUM(quantity)::NUMERIC , 50.0 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Quantity per planet' );
SELECT is( COUNT(*)::INT , 10 ) FROM test_result
WHERE quantity = 5;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total difficulty' );
SELECT is( SUM(difficulty)::NUMERIC , 7.5 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Difficulty per planet' );
SELECT is( COUNT(*)::INT , 10 ) FROM test_result
WHERE difficulty = 0.75;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total recovery rate' );
SELECT is( SUM(recovery)::NUMERIC , 5.0 ) FROM test_result;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Recovery rate per planet' );
SELECT is( COUNT(*)::INT , 10 ) FROM test_result
WHERE recovery = 0.5;
/* Second set of tests: balancing providers presence */
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in balanced universe' );
DELETE FROM test_result;
SELECT verse.create_resource_providers(
ROW( 0 , 0 , 1 , 1 ) ,
ROW( 42 , 20.0 , 10.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
);
SELECT is( COUNT(*)::INT , 10 ) FROM test_result WHERE resource = 42;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in universe with low providers count' );
DELETE FROM test_result;
SELECT verse.create_resource_providers(
ROW( 0 , 0 , 1 , 1 ) ,
ROW( 42 , 20.0 , 5.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
);
SELECT is( COUNT(*)::INT , 15 ) FROM test_result WHERE resource = 42;
SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in universe with high providers count' );
DELETE FROM test_result;
SELECT verse.create_resource_providers(
ROW( 0 , 0 , 1 , 1 ) ,
ROW( 42 , 20.0 , 15.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
);
SELECT is( COUNT(*)::INT , 5 ) FROM test_result WHERE resource = 42;
SELECT * FROM finish( );
ROLLBACK;

View file

@ -0,0 +1,35 @@
/*
* Test the "main" verse.create_resource_providers( ) function
*/
BEGIN;
/* Create a dummy rp_stats table that contains a few identifiers */
CREATE TEMP TABLE rp_stats
OF verse.resprov_generator_type
ON COMMIT DROP;
INSERT INTO rp_stats ( resource_name_id )
VALUES ( 1 ) , ( 2 ) , ( 3 ) , ( 4 );
/* Replace the verse.create_resource_providers( area , type ) function
* with a function that deletes the resource type it was given from
* rp_stats.
*/
CREATE OR REPLACE FUNCTION verse.create_resource_providers(
_area verse.generator_area_type ,
_data verse.resprov_generator_type )
RETURNS VOID
STRICT VOLATILE
SECURITY INVOKER
AS $create_resource_providers$
BEGIN
DELETE FROM rp_stats WHERE resource_name_id = _data.resource_name_id;
END;
$create_resource_providers$ LANGUAGE PLPGSQL;
SELECT plan(1);
SELECT diag_test_name( 'verse.create_resource_providers( area ) - Calls to per-type function' );
SELECT verse.create_resource_providers( ROW( 0 , 0 , 1 , 1 ) );
SELECT is( COUNT(*)::INT , 0 ) FROM rp_stats;
SELECT * FROM finish( );
ROLLBACK;