All Data Structures Files Functions Variables Enumerations Enumerator Properties Defines
/Projects/Cogito/src/GameObjects/CogitoAgents/DecisionTreeAgent.m
Go to the documentation of this file.
00001 //
00002 //  DecisionTreeAgent.m
00003 //  Author: Thomas Taylor
00004 //
00005 //  Handles the machine learning using Decision
00006 //  Tree learning
00007 //
00008 //  06/02/2012: Created class
00009 //
00010 
00011 #import "DecisionTreeAgent.h"
00012 
00013 @interface DecisionTreeAgent()
00014 
00015 -(void)setOptimumRoute;
00016 -(Action)getOptimumAction;
00017 
00018 @end
00019 
00020 @implementation DecisionTreeAgent
00021 
00022 #pragma mark -
00023 #pragma mark Memory Allocation
00024 
00028 -(void)dealloc
00029 {
00030     [levelTree release]; 
00031     [optimumRoute release];
00032     [super dealloc];
00033 }
00034 
00035 #pragma mark -
00036 #pragma mark Initialisation
00037 
00042 -(id)init
00043 {
00044     self = [super init];
00045     
00046     if (self != nil) 
00047     {
00048         optimumRoute = [[CCArray alloc] init];
00049         optimumRouteIndex = 0;
00050         currentAction = -1;
00051     }
00052     return self;
00053 }
00054 
00055 #pragma mark -
00056 
00062 -(Action)selectAction:(State*)_state
00063 {    
00064     // the action to return
00065     Action action = -1;
00066     
00067     // if the Agent's died/won
00068     BOOL endConditionReached = (self.state == kStateDead || [_state getGameObject].gameObjectType == kObjectExit);
00069     
00070     // create state, and get actions
00071     TreeState* treeState = [[TreeState alloc] initStateForObject:[_state getGameObject]];
00072     CCArray* options = [self calculateAvailableActions:treeState];
00073     
00074     // uses the Constant to randomise actions  
00075     int randomNumber = [Utils generateRandomNumberFrom:0 to:(int)(1/kLearningRandomProbability)];
00076     BOOL chooseRandom = (randomNumber == 0) ? chooseRandom = YES : NO;
00077     if(kLearningRandomProbability == 0.0f) chooseRandom = NO;
00078     
00079     // if still learning, randomly choose action
00080     if(learningMode || chooseRandom) 
00081     {   
00082         if(!endConditionReached) action = [self chooseRandomAction:options];  
00083               
00084         // if we're at the root node, don't create a new state
00085         if([_state getGameObject] == [levelTree getGameObject] && currentState == nil) currentState = levelTree;
00086         else
00087         {            
00088             if(levelTree == nil) levelTree = treeState;
00089             else
00090             {
00091                 [treeState setAction:currentAction];
00092                 [currentState addChild:treeState]; 
00093             }
00094             
00095             // update the current state/action variable
00096             currentState = treeState;
00097         }
00098         
00099         currentAction = action;
00100             
00101         if(endConditionReached)
00102         {
00103             // if we've reached an end condition, make a leaf node
00104             if(self.state == kStateDead) [treeState setAsLeafNode:kStateDead];
00105             else if([_state getGameObject].gameObjectType == kObjectExit) [treeState setAsLeafNode:kStateWin];
00106             
00107             // reset the current node
00108             currentState = nil;
00109         }        
00110     }
00111     // not learning, choose the optimum action (providing not dead/won)
00112     else if(!endConditionReached) 
00113     {
00114         action = [self getOptimumAction];
00115         
00116         // no data for the current state, choose random action
00117         if(action == -1) action = [self chooseRandomAction:options];  
00118     }
00119     
00120     return action;
00121 }
00122 
00127 -(CCArray*)getShortestRoutes
00128 {    
00129     // build the routes
00130     CCArray* routes = [CCArray arrayWithCapacity:0];
00131     [levelTree buildRoutes:routes];
00132     
00133     // if no optimum routes are found, exit
00134     if([routes count] == 0) return nil;
00135     
00136     // find the shortest route
00137     CCArray* route = [routes objectAtIndex:0];
00138     
00139     for (int i = 1; i < [routes count]; i++) 
00140         if([[routes objectAtIndex:i] count] < [route count]) route = [routes objectAtIndex:i];
00141     
00142     // check through the routes for other shortest routes
00143     for (int i = 1; i < [routes count]; i++) 
00144         if([[routes objectAtIndex:i] count] > [route count]) [routes removeObjectAtIndex:i];
00145     
00146     return routes;
00147 }
00148 
00152 -(void)setOptimumRoute
00153 {    
00154     // build the routes
00155     
00156     CCArray* routes = [self getShortestRoutes];
00157     
00158     // if no optimum routes are found, exit
00159     if([routes count] == 0) return;
00160     
00161     // calculate the cost of each route (cost = +1 for every tool use)
00162     
00163     NSMutableDictionary* routesWithCosts = [NSMutableDictionary dictionaryWithCapacity:[routes count]];
00164     
00165     for (int i = 0; i < [routes count]; i++) 
00166     {
00167         CCArray* route = [routes objectAtIndex:i];
00168         int routeCost = 0;
00169         
00170         for (int i = 0; i < [route count]; i++) 
00171             if([[route objectAtIndex:i] getAction] == kActionDownUmbrella ||
00172                [[route objectAtIndex:i] getAction] == kActionEquipUmbrella ||
00173                [[route objectAtIndex:i] getAction] == kActionLeftHelmet ||
00174                [[route objectAtIndex:i] getAction] == kActionRightHelmet) 
00175                     routeCost++;
00176         
00177         [routesWithCosts setObject:[NSNumber numberWithInt:routeCost] forKey:route];
00178     }
00179     
00180     
00181     // find the route with the lowest cost
00182     
00183     int minimumCost = 999;      // the lowest cost so far
00184     CCArray* minimumCostRoute;
00185     
00186     for (CCArray* route in routesWithCosts) 
00187     {
00188         if([[routesWithCosts objectForKey:route] intValue] < minimumCost)
00189         {
00190             CCLOG(@"Route cost: %i", [[routesWithCosts objectForKey:route] intValue]);
00191             minimumCostRoute = route;
00192             minimumCost = [[routesWithCosts objectForKey:route] intValue];
00193         }
00194     }
00195     
00196     CCLOG(@"%@.setOptimumRoute: routes: %i mimumumCost: %i minimumCostRoute: %i", NSStringFromClass([self class]), [routes count], minimumCost, [minimumCostRoute count]);
00197     
00198     // finally, the array needs to be reversed
00199     for (int i = [minimumCostRoute count]-1; i > -1; i--) 
00200         [optimumRoute addObject:[minimumCostRoute objectAtIndex:i]];
00201     
00202     CCLOG(@"Optimum route is:");
00203     for (int i = 0; i < [optimumRoute count]; i++) CCLOG(@"   %@", [Utils getActionAsString:[[optimumRoute objectAtIndex:i] getAction]]);
00204 }
00205 
00210 -(Action)getOptimumAction
00211 {
00212     Action action = -1;
00213     
00214     if([optimumRoute count] > 0)
00215     {
00216         action = [[optimumRoute objectAtIndex:optimumRouteIndex] getAction];
00217         optimumRouteIndex++;
00218     }
00219         
00220     return action;
00221 }
00222 
00223 #pragma mark -
00224 #pragma mark Overrides
00225 
00230 -(void)onEndConditionReached
00231 {                
00232     if(learningMode && respawns < 2) [self setOptimumRoute];
00233     [super onEndConditionReached];
00234 }
00235 
00239 -(void)updateDebugLabel
00240 {    
00241     [super updateDebugLabel];
00242     
00243     if(!(learningMode || self.state == kStateDead || self.state == kStateWin))
00244     {
00245         [debugLabel setString:([optimumRoute count] > 0) ? @"!" : @"?"];
00246     }
00247 }
00248 
00249 @end