///////////////////////////////////////////////////////////////////////////////
//
// A boolean version of the wildfire fighting domain.
//
// Author: Zhenyu Yu (fry3721@gmail.com)
//
// General reference:
// 
//   Karafyllidis, I., & Thanailakis, A. (1997). 
//   A model for predicting forest fire spreading using gridular automata. 
//   Ecological Modelling, 99(1), 87-97.
//   http://www.dpi.inpe.br/gilberto/cursos/st-society-2013/Kara1997.pdf
//
// In a general wildfire scenario, its spread is mostly determined by 
// the weather (i.e. wind), terrain slope, and fuel type (i.e. grass, wood).
// In this scenario, a map is represented with grids, size of n*n.
// Each grid has some attributes, including fuel type, terrain elevation.
// Furthermore, the fuel type and terrain elevation will affect the fire 
// spreading speed.  Some fuel type is more easily on fire than other, 
// and higher grids are always easier to catch fire.  Cell features and
// effects of wind are not modeled in this simplified version.
//
// In this version, whether a cell would be on fire is determined by its 
// neighbor grids, and the fire spreading law is simplified with this function
//
//   p(burning(xi, yj)=true) = 1 / (1 + exp(4.5 - k))
//
// where k is the number of neighbors on fire.
//
// The decision task to a emergency manager is to control the fire 
// and keep it away from important targets.
//
// Modified for competition and translation purposes by Scott Sanner.
//  
///////////////////////////////////////////////////////////////////////////////

domain wildfire_mdp {

	types {
		x_pos : object;
		y_pos : object;
	};
	
	pvariables {
	
		// Action costs and penalties
		COST_CUTOUT            : {non-fluent, real, default =   -5 }; // Cost to cut-out fuel from a cell
		COST_PUTOUT            : {non-fluent, real, default =  -10 }; // Cost to put-out a fire from a cell
		PENALTY_TARGET_BURN    : {non-fluent, real, default = -100 }; // Penalty for each target cell that is burning  
		PENALTY_NONTARGET_BURN : {non-fluent, real, default =   -5 }; // Penalty for each non-target cell that is burning
	
		// Topology of the cells (can be any neighborhood topology, not necessarily rectangular)
		NEIGHBOR(x_pos, y_pos, x_pos, y_pos) : { non-fluent, bool, default = false };
		
		// High value cells that should be protected from fire 
		TARGET(x_pos, y_pos) : {non-fluent, bool, default = false }; 
		
		// State fluents
		burning(x_pos, y_pos)     : { state-fluent, bool, default = false }; // cell currently on fire
		out-of-fuel(x_pos, y_pos) : { state-fluent, bool, default = false }; // cell does not have fuel to burn (i.e., cut-out or already burned)
		
		// Action fluents
		put-out(x_pos, y_pos) : { action-fluent, bool, default = false }; // actions to put-out out the fire
		cut-out(x_pos, y_pos) : { action-fluent, bool, default = false }; // cut-out out the fuel
			
	};

	cpfs {
		
		burning'(?x, ?y) = 
			if ( put-out(?x, ?y) ) // Intervention to put out fire?
				then false
            // Modification: targets can only start to burn if at least one neighbor is on fire
            else if (~out-of-fuel(?x, ?y) ^ ~burning(?x, ?y)) // Ignition of a new fire? Depends on neighbors.
              then [if (TARGET(?x, ?y) ^ ~exists_{?x2: x_pos, ?y2: y_pos} (NEIGHBOR(?x, ?y, ?x2, ?y2) ^ burning(?x2, ?y2)))
                    then false
                    else Bernoulli( 1.0 / (1.0 + exp[4.5 - (sum_{?x2: x_pos, ?y2: y_pos} (NEIGHBOR(?x, ?y, ?x2, ?y2) ^ burning(?x2, ?y2)))]) ) ]
			else 
				burning(?x, ?y); // State persists
		
		// Modification: only allow non-target cells to be cut-out (cannot remove fuel from targets, e.g., housing)
		out-of-fuel'(?x, ?y) = out-of-fuel(?x, ?y) | burning(?x,?y) | (~TARGET(?x, ?y) ^ cut-out(?x, ?y));
						
	};
    	
	reward = 
 	    [sum_{?x: x_pos, ?y: y_pos} [ COST_CUTOUT*cut-out(?x, ?y) ]]
 	  + [sum_{?x: x_pos, ?y: y_pos} [ COST_PUTOUT*put-out(?x, ?y) ]]
 	    // Modification: if a target is out-of-fuel, it was burnt so still penalize (since it could not have been cut-out)
 	  + [sum_{?x: x_pos, ?y: y_pos} [ PENALTY_TARGET_BURN*[ (burning(?x, ?y) | out-of-fuel(?x, ?y)) ^ TARGET(?x, ?y) ]]]
 	  + [sum_{?x: x_pos, ?y: y_pos} [ PENALTY_NONTARGET_BURN*[ burning(?x, ?y) ^ ~TARGET(?x, ?y) ]]];

}




