- Fix issue 194
- Attempt at doing basic AI tests
This commit is contained in:
wagic.the.homebrew@gmail.com
2009-11-15 09:31:33 +00:00
parent 072c3d15d5
commit f6a75a0e43
18 changed files with 277 additions and 140 deletions

View File

@@ -73,14 +73,13 @@ beacon_of_unrest.txt
belligerent_hatchling.txt
benalish_knight.txt
blessed_wine.txt
blinking_spirit.txt
bloodfire_colossus.txt
bloodhall_ooze.txt
body_double.txt
boggart_arsonists.txt
borderland_behemoth.txt
bottle_gnomes.txt
bottle_gnomes2.txt
boggart_arsonists.txt
brass_man.txt
cage_of_hands.txt
Call_to_Heel_1.txt
@@ -105,8 +104,8 @@ counterspell.txt
counterspell2.txt
counterspell3.txt
counterspell4.txt
creature_bond.txt
crimson_kobolds.txt
creature_bond.txt
dauntless_escort.txt
dauthi_embrace.txt
death_ward.txt
@@ -127,17 +126,16 @@ elvish_promenade.txt
emblem_of_the_warmind.txt
enervate.txt
erg_raiders_i157.txt
farhaven_elf.txt
farmstead.txt
flagstones.txt
farhaven_elf.txt
fastbond.txt
fastbond2.txt
fault_line.txt
fists_of_ironwood.txt
flagstones.txt
flare.txt
fledgling_imp.txt
fledgling_imp2.txt
flowstone_hellion.txt
flowstone_slide.txt
foratog.txt
force_of_nature.txt
@@ -160,14 +158,12 @@ harpoon_sniper.txt
hellfire.txt
howl_from_beyond.txt
howl_of_the_night_pack.txt
hyalopterous_lemure.txt
hymn_of_rebirth.txt
hypnotic_specter.txt
icatian_priest.txt
imaginary_pet.txt
immaculate_magistrate.txt
instill_energy_i166.txt
jodahs_avenger.txt
jump.txt
keldon_warlord.txt
keldon_warlord2.txt
@@ -239,26 +235,26 @@ seedcradle_witch.txt
seismic_assault.txt
seismic_spike_i191.txt
selesnya_guildmage.txt
siege_gang_commander.txt
shepherd_of_rot.txt
shivan_hellkite.txt
shock.txt
shock2.txt
siege_gang_commander.txt
slate_of_ancestry.txt
sleeper_agent.txt
slith_bloodletter.txt
soulblast.txt
spark_elemental.txt
sphinx_summoner.txt
spirit_link.txt
spitting_earth.txt
spark_elemental.txt
spirit_link.txt
spoils_of_evil.txt
stasis.txt
steelclad_serpent1.txt
steelclad_serpent2.txt
stillmoon_cavalier.txt
stonebrow2.txt
stonebrow_i159.txt
stonebrow2.txt
stronghold_discipline.txt
sword_to_plowshares.txt
symbiotic_wurm.txt
@@ -271,9 +267,10 @@ titanic_ultimatum.txt
torture.txt
tranquil_domain.txt
twitch.txt
unstable_mutation.txt
unstable_mutation2.txt
unwilling_recruit.txt
unstable_mutation.txt
unstable_mutation2.txt
vampire_bats.txt
vampiric_link.txt
volcanic_eruption.txt
@@ -292,3 +289,7 @@ zombify.txt
########################
momir/keldon_warlord.txt
momir/overcost.txt
########################
#AI Tests
########################
ai/goblin_artillery.txt

View File

@@ -0,0 +1,22 @@
#Bug:ai uses goblin artilery on own creature
# http://code.google.com/p/wagic/issues/detail?id=194
FORCEABILITY
RValues:2
AICALLS 10
[INIT]
FIRSTMAIN
[PLAYER1]
inplay:,grizzly bears,goblin artillery
[PLAYER2]
inplay:raging goblin
[DO]
ai
ai
[ASSERT]
FIRSTMAIN
[PLAYER1]
life:17
inplay:goblin artillery,grizzly bears
[PLAYER2]
graveyard:raging goblin
[END]

View File

@@ -21,16 +21,20 @@ class AIStats;
class AIAction{
protected:
int efficiency;
static int currentId;
static MTGAbility * getCoreAbility(MTGAbility * a);
public:
MTGAbility * ability;
Player * player;
int id;
MTGCardInstance * click;
MTGCardInstance * target; // TODO Improve
AIAction(MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL):ability(a),click(c),target(t){player = NULL; efficiency = -1;};
AIAction(MTGCardInstance * c, MTGCardInstance * t = NULL):click(c),target(t){player = NULL; ability = NULL; efficiency = -1;};
AIAction(MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL):ability(a),click(c),target(t){player = NULL; efficiency = -1; id = currentId++;};
AIAction(MTGCardInstance * c, MTGCardInstance * t = NULL):click(c),target(t){player = NULL; ability = NULL; efficiency = -1; id = currentId++;};
AIAction(Player * p):player(p){ability = NULL; target = NULL; click = NULL; efficiency = -1;};
int getEfficiency();
int Act();
};
@@ -39,7 +43,7 @@ class CmpAbilities { // compares Abilities efficiency
bool operator()(AIAction * a1, AIAction * a2) const {
int e1 = a1->getEfficiency();
int e2 = a2->getEfficiency();
if (e1 == e2) return a1 > a2; //TODO improve
if (e1 == e2) return a1->id < a2->id;
return (e1 > e2);
}
};
@@ -59,7 +63,9 @@ class AIPlayer: public Player{
int effectBadOrGood(MTGCardInstance * card, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL);
int getCreaturesInfo(Player * player, int neededInfo = INFO_NBCREATURES , int untapMode = 0, int canAttack = 0);
AIStats * getStats();
//Variables used by Test suite
public:
bool forceBestAbilityUse;
void End(){};
virtual int displayStack() {return 0;};
int receiveEvent(WEvent * event);

View File

@@ -48,6 +48,7 @@ class GameStateDuel: public GameState, public JGuiListener
virtual void End();
virtual void Update(float dt);
virtual void Render();
void initRand (unsigned seed = 0);
};

View File

@@ -51,8 +51,11 @@ class TestSuite{
public:
MTGAllCards* collection;
int summoningSickness;
bool forceAbility;
int gameType;
float timerLimit;
unsigned int seed;
int aiMaxCalls;
int currentAction;
TestSuiteState initState;
TestSuiteState endState;
@@ -60,7 +63,7 @@ class TestSuite{
string files[1024];
int nbfiles;
int currentfile;
int nbFailed, nbTests;
int nbFailed, nbTests, nbAIFailed, nbAITests;
int load(const char * filename);
TestSuite(const char * filename,MTGAllCards* _collection);
void initGame();

View File

@@ -24,6 +24,7 @@
#include <math.h>
#include <stdio.h>
#include <string>
#include <fstream>
#include <iostream>
#include <algorithm>
@@ -34,8 +35,12 @@ using std::string;
int loadRandValues(string s);
int filesize(const char * filename);
int fileExists(const char * filename);
int WRand();
#ifdef LINUX
void dumpStack();

View File

@@ -40,7 +40,7 @@ int AIMomirPlayer::momir(){
int converted = potentialMana->getConvertedCost();
SAFE_DELETE(potentialMana);
int efficiency = 100;
int chance = 1 + (rand() % 100);
int chance = 1 + (WRand() % 100);
if (converted == 5 && myCreatures > opponentCreatures && game->hand->nb_cards<4) efficiency = 5 ; //Strategy: skip 5 drop
if (converted == 7 && myCreatures > opponentCreatures && game->hand->nb_cards<2) efficiency = 50; //Strategy: 7 drops have bad upkeep costs and the AI doesn't handle those right now...
if (converted > 8 ) converted = 8;

View File

@@ -8,6 +8,8 @@
const char * const MTG_LAND_TEXTS[] = {"artifact","forest","island","mountain","swamp","plains","other lands"};
int AIAction::currentId = 0;
int AIAction::Act(){
GameObserver * g = GameObserver::GetInstance();
if (player){
@@ -30,6 +32,7 @@ AIPlayer::AIPlayer(MTGPlayerCards * deck, string file, string fileSmall) : Playe
nextCardToPlay = NULL;
stats = NULL;
agressivity = 50;
forceBestAbilityUse = false;
}
AIPlayer::~AIPlayer(){
@@ -139,6 +142,18 @@ int AIPlayer::canHandleCost(MTGAbility * ability){
return 1;
}
MTGAbility * AIAction::getCoreAbility(MTGAbility * a){
GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*>(a);
if (gta) return getCoreAbility(gta->ability);
GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*>(a);
if (gaa) return getCoreAbility(gaa->ability);
if (MultiAbility * abi = dynamic_cast<MultiAbility*>(a)) return getCoreAbility(abi->abilities[0]);
return a;
}
int AIAction::getEfficiency(){
//TODO add multiplier according to what the player wants
if (efficiency != -1) return efficiency;
@@ -148,12 +163,7 @@ int AIAction::getEfficiency(){
Player * p = g->currentlyActing();
if (s->has(ability)) return 0;
MTGAbility * a = ability;
GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*>(a);
if (gta) a = gta->ability;
GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*>(a);
if (gaa) a = gaa->ability;
MTGAbility * a = getCoreAbility(ability);
if (!a){
OutputDebugString("FATAL: Ability is NULL in AIAction::getEfficiency()");
@@ -197,10 +207,10 @@ int AIAction::getEfficiency(){
if ((suggestion == BAKA_EFFECT_BAD && p==target->controller()) ||(suggestion == BAKA_EFFECT_GOOD && p!=target->controller())){
efficiency =0;
}else{
efficiency = rand() % 5; //Small percentage of chance for unknown abilities
efficiency = WRand() % 5; //Small percentage of chance for unknown abilities
}
}else{
efficiency = rand() % 10;
efficiency = WRand() % 10;
}
break;
}
@@ -262,7 +272,8 @@ int AIPlayer::selectAbility(){
if (ranking.size()){
AIAction * a = ranking.begin()->first;
int chance = 1 + rand() % 100;
int chance = 1;
if (!forceBestAbilityUse) chance = 1 + WRand() % 100;
if (getEfficiency(a) < chance){
a = NULL;
}else{
@@ -354,7 +365,7 @@ int AIPlayer::chooseTarget(TargetChooser * tc){
}
}
if (nbtargets){
int i = rand() % nbtargets;
int i = WRand() % nbtargets;
int type = potentialTargets[i]->typeAsTarget();
switch(type){
case TARGET_CARD:
@@ -575,7 +586,7 @@ AIPlayer * AIPlayerFactory::createAIPlayer(MTGAllCards * collection, Player * op
}
}
if (!nbdecks) return NULL;
deckid = 1 + rand() % (nbdecks);
deckid = 1 + WRand() % (nbdecks);
}
sprintf(deckFile, RESPATH"/ai/baka/deck%i.txt",deckid);
sprintf(avatarFile, "avatar%i.jpg",deckid);
@@ -628,7 +639,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
shouldPlayPercentage = shouldPlayPercentage - ((shouldPlayPercentage * 1.9) / (1 + xDiff));
}
if (rand() % 100 > shouldPlayPercentage) continue;
if (WRand() % 100 > shouldPlayPercentage) continue;
nextCardToPlay = card;
maxCost = currentCost;
if(hasX) maxCost = pMana->getConvertedCost();

View File

@@ -38,7 +38,7 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app){
p1 = _p1;
p2 = _p2;
app = _app;
showMsg = (rand() % 5);
showMsg = (WRand() % 5);
GameObserver * g = GameObserver::GetInstance();
if (!g->turn) return;
if (!p1->isAI() && p2->isAI() && p1!= g->gameOver){
@@ -238,7 +238,7 @@ int Credits::isRandomDeckUnlocked(){
}
int Credits::unlockRandomSet(){
int setId = rand() % MtgSets::SetsList->nb_items;
int setId = WRand() % MtgSets::SetsList->nb_items;
if (1 == options[Options::optionSet(setId)].number)
return 0;

View File

@@ -2,6 +2,7 @@
#include <JGE.h>
#include <JRenderer.h>
#if defined (WIN32) || defined (LINUX)
#include <time.h>
#else
#include <pspfpu.h>
#endif
@@ -65,6 +66,7 @@ GameApp::~GameApp()
void GameApp::Create()
{
srand(time(0)); // initialize random
#if defined (WIN32)
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#elif not defined (LINUX)

View File

@@ -14,6 +14,10 @@
#include "../include/TestSuiteAI.h"
#endif
#if defined (WIN32) || defined (LINUX)
#include <time.h>
#endif
enum ENUM_DUEL_STATE
{
DUEL_STATE_START,
@@ -103,8 +107,8 @@ void GameStateDuel::Start()
}
void GameStateDuel::loadPlayerRandom(int playerId, int isAI, int mode){
int color1 = 1 + rand() % 5;
int color2 = 1 + rand() % 5;
int color1 = 1 + WRand() % 5;
int color2 = 1 + WRand() % 5;
int color0 = Constants::MTG_COLOR_ARTIFACT;
if (mode == GAME_TYPE_RANDOM1) color2 = color1;
int colors[]={color1,color2,color0};
@@ -178,9 +182,15 @@ void GameStateDuel::loadPlayer(int playerId, int decknb, int isAI){
}
}
void GameStateDuel::initRand(unsigned int seed){
if (!seed) seed = time(0);
srand(seed);
}
#ifdef TESTSUITE
void GameStateDuel::loadTestSuitePlayers(){
if (!testSuite) return;
initRand(testSuite->seed);
for (int i = 0; i < 2; i++){
SAFE_DELETE(mPlayers[i]);
SAFE_DELETE(deck[i]);
@@ -407,8 +417,15 @@ void GameStateDuel::Render()
}else{
sprintf(buf, "%i tests out of %i FAILED!", nbFailed, nbTests);
}
mFont->DrawString(buf,0,SCREEN_HEIGHT/2);
nbFailed = testSuite->nbAIFailed;
nbTests = testSuite->nbAITests;
if (!nbFailed){
sprintf(buf, "AI Tests: All %i tests successful!", nbTests);
}else{
sprintf(buf, "AI Tests: %i tests out of %i FAILED!", nbFailed, nbTests);
}
mFont->DrawString(buf,0,SCREEN_HEIGHT/2+20);
}
#endif
break;

View File

@@ -290,7 +290,7 @@ void GameStateMenu::loadLangMenu(){
mDip = opendir("Res/lang");
}
while (mDit = readdir(mDip)){
while ((mDit = readdir(mDip))){
string filename = "Res/lang/";
filename += mDit->d_name;
std::ifstream file(filename.c_str());

View File

@@ -187,7 +187,6 @@ void MTGAllCards::init(){
tempCard = NULL;
total_cards = 0;
initCounters();
srand(time(0)); // initialize random
#if defined (_DEBUG)
committed = true;
#endif

View File

@@ -160,7 +160,7 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone
void MTGPlayerCards::discardRandom(MTGGameZone * from){
if (!from->nb_cards)
return;
int r = rand() % (from->nb_cards);
int r = WRand() % (from->nb_cards);
putInZone(from->cards[r],from, graveyard);
}
@@ -263,10 +263,9 @@ void MTGGameZone::cleanupPhase(){
void MTGGameZone::shuffle(){
int i;
for (i=0; i<(nb_cards); i++) {
int r = i + (rand() % (nb_cards-i)); // Random remaining position.
int r = i + (WRand() % (nb_cards-i)); // Random remaining position.
MTGCardInstance * temp = cards[i]; cards[i] = cards[r]; cards[r] = temp;
}
//srand(time(0)); // initialize seed "randomly" TODO :improve
}
@@ -365,7 +364,7 @@ void MTGLibrary::shuffleTopToBottom(int nbcards){
if (nbcards>nb_cards) nbcards = nb_cards;
MTGCardInstance * _cards[MTG_MAX_PLAYER_CARDS];
for (int i= nb_cards-nbcards; i<(nb_cards); i++) {
int r = i + (rand() % (nbcards-i)); // Random remaining position.
int r = i + (WRand() % (nbcards-i)); // Random remaining position.
MTGCardInstance * temp = cards[i]; cards[i] = cards[r]; cards[r] = temp;
}
for (int i= 0; i < nbcards; i++){

View File

@@ -304,7 +304,7 @@ int MTGMomirRule::genRandomCreatureId(int convertedCost){
i--;
}
if (!total_cards) return 0;
int start = (rand() % total_cards);
int start = (WRand() % total_cards);
return pool[convertedCost][start];
}

View File

@@ -68,7 +68,11 @@ int TestSuiteAI::displayStack(){
int TestSuiteAI::Act(float dt){
GameObserver * g = GameObserver::GetInstance();
g->gameOver = NULL; // Prevent draw rule from losing the game
if (playMode == MODE_AI) return AIPlayerBaka::Act(dt);
if (playMode == MODE_AI && suite->aiMaxCalls) {
suite->aiMaxCalls--;
suite->timerLimit = 40; //TODO Remove this limitation when AI is not using a stupid timer anymore...
AIPlayerBaka::Act(dt);
}
if (playMode == MODE_HUMAN){
g->mLayers->CheckUserInput(0);
return 1;
@@ -95,7 +99,7 @@ int TestSuiteAI::Act(float dt){
}
}
if (action == ""){
if (action == ""){
//end of game
suite->assertGame();
g->gameOver = g->players[0];
@@ -284,7 +288,8 @@ void TestSuite::initGame(){
g->phaseRing->goToPhase(initState.phase, g->players[0]);
g->currentGamePhase = initState.phase;
for (int i = 0; i < 2; i++){
Player * p = g->players[i];
AIPlayer * p = (AIPlayer *) (g->players[i]);
p->forceBestAbilityUse = forceAbility;
p->life = initState.playerData[i].life;
p->getManaPool()->copy(initState.playerData[i].manapool);
MTGGameZone * playerZones[] = {p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay};
@@ -335,6 +340,8 @@ int TestSuite::assertGame(){
Log(result);
int error = 0;
bool wasAI = false;
GameObserver * g = GameObserver::GetInstance();
if (g->currentGamePhase != endState.phase){
sprintf(result, "<span class=\"error\">==phase problem. Expected %i, got %i==</span><br />",endState.phase, g->currentGamePhase);
@@ -342,7 +349,9 @@ int TestSuite::assertGame(){
error++;
}
for (int i = 0; i < 2; i++){
Player * p = g->players[i];
TestSuiteAI * p = (TestSuiteAI *)(g->players[i]);
if (p->playMode == MODE_AI) wasAI = true;
if (p->life != endState.playerData[i].life){
sprintf(result, "<span class=\"error\">==life problem for player %i. Expected %i, got %i==</span><br />",i,endState.playerData[i].life, p->life);
Log(result);
@@ -380,10 +389,18 @@ int TestSuite::assertGame(){
}
}
}
nbTests++;
if (error) {
nbFailed++;
return 0;
if (wasAI) {
nbAITests++;
if (error) {
nbAIFailed++;
return 0;
}
} else {
nbTests++;
if (error) {
nbFailed++;
return 0;
}
}
Log("<span class=\"success\">==Test Succesful !==</span>");
return 1;
@@ -398,7 +415,12 @@ TestSuite::TestSuite(const char * filename,MTGAllCards* _collection){
currentfile = 0;
nbFailed = 0;
nbTests = 0;
nbAIFailed = 0;
nbAITests = 0;
int comment = 0;
seed = 0;
forceAbility = false;
aiMaxCalls = -1;
if(file){
while(std::getline(file,s)){
if (!s.size()) continue;
@@ -430,6 +452,8 @@ TestSuite::TestSuite(const char * filename,MTGAllCards* _collection){
int TestSuite::loadNext(){
summoningSickness = 0;
seed = 0;
aiMaxCalls = -1;
if (!nbfiles) return 0;
if (currentfile >= nbfiles) return 0;
currentfile++;
@@ -489,15 +513,18 @@ void TestSuite::cleanup(){
initState.cleanup();
endState.cleanup();
actions.cleanup();
loadRandValues("");
}
int TestSuite::load(const char * _filename){
summoningSickness = 0;
forceAbility = false;
gameType = GAME_TYPE_CLASSIC;
char filename[4096];
sprintf(filename, RESPATH"/test/%s", _filename);
std::ifstream file(filename);
std::string s;
loadRandValues("");
int state = -1;
@@ -513,6 +540,22 @@ int TestSuite::load(const char * _filename){
summoningSickness = 1;
continue;
}
if (s.compare("forceability") == 0) {
forceAbility = true;
continue;
}
if (s.find("seed ") == 0) {
seed = atoi(s.substr(5).c_str());
continue;
}
if (s.find("rvalues:") == 0) {
loadRandValues(s.substr(8).c_str());
continue;
}
if (s.find("aicalls ") == 0) {
aiMaxCalls = atoi(s.substr(8).c_str());
continue;
}
if (s.compare("momir") == 0) {
gameType = GAME_TYPE_MOMIR;
continue;

View File

@@ -1,5 +1,5 @@
#include "../include/TextScroller.h"
#include "../include/utils.h"
#include <JLBFont.h>
TextScroller::TextScroller(JLBFont * font, float x, float y, float width, float speed):JGuiObject(0){

View File

@@ -1,9 +1,37 @@
#include "../include/config.h"
#include "../include/utils.h"
#include <vector>
using std::vector;
int randValuesCursor = -1;
vector<int>randValues;
int loadRandValues(string s){
randValues.clear();
randValuesCursor = -1;
while (s.size()){
unsigned int value;
size_t limiter = s.find(",");
if (limiter != string::npos){
value = atoi(s.substr(0,limiter).c_str());
s = s.substr(limiter+1);
}else{
value = atoi(s.c_str());
s = "";
}
if (value) randValues.push_back(value);
}
if (randValues.size()) randValuesCursor = 0;
return 1;
}
int WRand(){
if (randValuesCursor == -1) return rand();
int result = randValues[randValuesCursor];
randValuesCursor++;
if ((size_t)randValuesCursor >= randValues.size()) randValuesCursor = 0;
return result;
}
int filesize(const char * filename){
int file_size = 0;
@@ -60,92 +88,92 @@ void dumpStack()
}
#endif
/* RAM simple check functions source */
// *** FUNCTIONS ***
u32 ramAvailableLineareMax (void)
{
u32 size, sizeblock;
u8 *ram;
// Init variables
size = 0;
sizeblock = RAM_BLOCK;
// Check loop
while (sizeblock)
{
// Increment size
size += sizeblock;
// Allocate ram
ram = (u8 *) malloc(size);
// Check allocate
if (!(ram))
{
// Restore old size
size -= sizeblock;
// Size block / 2
sizeblock >>= 1;
}
else
free(ram);
}
return size;
}
u32 ramAvailable (void)
{
u8 **ram, **temp;
u32 size, count, x;
// Init variables
ram = NULL;
size = 0;
count = 0;
// Check loop
for (;;)
{
// Check size entries
if (!(count % 10))
{
// Allocate more entries if needed
temp = (u8**) realloc(ram,sizeof(u8 *) * (count + 10));
if (!(temp)) break;
// Update entries and size (size contains also size of entries)
ram = temp;
size += (sizeof(u8 *) * 10);
}
// Find max lineare size available
x = ramAvailableLineareMax();
if (!(x)) break;
// Allocate ram
ram[count] = (u8 *) malloc(x);
if (!(ram[count])) break;
// Update variables
size += x;
count++;
}
// Free ram
if (ram)
{
for (x=0;x<count;x++) free(ram[x]);
free(ram);
}
return size;
/* RAM simple check functions source */
// *** FUNCTIONS ***
u32 ramAvailableLineareMax (void)
{
u32 size, sizeblock;
u8 *ram;
// Init variables
size = 0;
sizeblock = RAM_BLOCK;
// Check loop
while (sizeblock)
{
// Increment size
size += sizeblock;
// Allocate ram
ram = (u8 *) malloc(size);
// Check allocate
if (!(ram))
{
// Restore old size
size -= sizeblock;
// Size block / 2
sizeblock >>= 1;
}
else
free(ram);
}
return size;
}
u32 ramAvailable (void)
{
u8 **ram, **temp;
u32 size, count, x;
// Init variables
ram = NULL;
size = 0;
count = 0;
// Check loop
for (;;)
{
// Check size entries
if (!(count % 10))
{
// Allocate more entries if needed
temp = (u8**) realloc(ram,sizeof(u8 *) * (count + 10));
if (!(temp)) break;
// Update entries and size (size contains also size of entries)
ram = temp;
size += (sizeof(u8 *) * 10);
}
// Find max lineare size available
x = ramAvailableLineareMax();
if (!(x)) break;
// Allocate ram
ram[count] = (u8 *) malloc(x);
if (!(ram[count])) break;
// Update variables
size += x;
count++;
}
// Free ram
if (ram)
{
for (x=0;x<count;x++) free(ram[x]);
free(ram);
}
return size;
}