add auto mode moving - integrate astar, neural network and decision tree
This commit is contained in:
parent
f07eabf522
commit
8851493d9d
17
Readme.md
17
Readme.md
@ -10,6 +10,12 @@ virtualenv venv
|
|||||||
source activate
|
source activate
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
#### 2.1 Graphviz
|
||||||
|
For ubuntu, we probably need to download graphviz library by apt-get
|
||||||
|
```bash
|
||||||
|
sudo apt-get install graphviz
|
||||||
|
```
|
||||||
|
|
||||||
### 3. Run application
|
### 3. Run application
|
||||||
```bash
|
```bash
|
||||||
python3 main.py
|
python3 main.py
|
||||||
@ -37,13 +43,20 @@ HORIZONTAL_NUM_OF_FIELDS = 3
|
|||||||
|
|
||||||
#### 4.1 Save generated map:
|
#### 4.1 Save generated map:
|
||||||
```bash
|
```bash
|
||||||
python main.py --save-map
|
python3 main.py --save-map
|
||||||
```
|
```
|
||||||
Map will be saved in maps directory.
|
Map will be saved in maps directory.
|
||||||
Generated filename: map-uuid
|
Generated filename: map-uuid
|
||||||
|
|
||||||
#### 4.2 Load map
|
#### 4.2 Load map
|
||||||
```bash
|
```bash
|
||||||
python main.py --load-map=name_of_map
|
python3 main.py --load-map=name_of_map
|
||||||
```
|
```
|
||||||
Map must be in the maps directory with json extension.
|
Map must be in the maps directory with json extension.
|
||||||
|
|
||||||
|
#### 4.3 Auto mode
|
||||||
|
Tractor will make own decisions such as harvesting, hydrating and so on using a decision tree.
|
||||||
|
It also will be moving by a star algorithm and it will be checking fields using a neural network during harvesting crops.
|
||||||
|
```bash
|
||||||
|
python3 main.py --auto-mode
|
||||||
|
```
|
@ -24,6 +24,13 @@ class App:
|
|||||||
self.__tractor = Tractor(self.__board)
|
self.__tractor = Tractor(self.__board)
|
||||||
self.__bot_is_running = Event()
|
self.__bot_is_running = Event()
|
||||||
self.__moves = None
|
self.__moves = None
|
||||||
|
self.__auto_mode = self.get_auto_mode_arg(args)
|
||||||
|
|
||||||
|
def get_auto_mode_arg(self, args: list[Arg]):
|
||||||
|
for arg in args:
|
||||||
|
if arg.get_arg_name() == AUTO_MODE:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
pygame.init()
|
pygame.init()
|
||||||
@ -79,7 +86,7 @@ class App:
|
|||||||
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
|
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
|
||||||
|
|
||||||
if keys[pygame.K_c]:
|
if keys[pygame.K_c]:
|
||||||
self.get_moves_by_a_star()
|
self.get_moves_by_a_star_with_random_coords()
|
||||||
if not self.__moves:
|
if not self.__moves:
|
||||||
print(f"A Star is failed")
|
print(f"A Star is failed")
|
||||||
else:
|
else:
|
||||||
@ -88,7 +95,7 @@ class App:
|
|||||||
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
|
self.__tractor.run_bot_handler(self.__moves, self.__bot_is_running)
|
||||||
|
|
||||||
if keys[pygame.K_a]:
|
if keys[pygame.K_a]:
|
||||||
self.auto_moving()
|
self.auto_moving_mode()
|
||||||
|
|
||||||
def update_screen(self) -> None:
|
def update_screen(self) -> None:
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
@ -96,19 +103,46 @@ class App:
|
|||||||
def quit(self) -> None:
|
def quit(self) -> None:
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
||||||
def auto_moving(self) -> None:
|
def auto_moving_mode(self):
|
||||||
self.__tractor.choose_action()
|
coords, action = self.get_coords_and_action()
|
||||||
|
|
||||||
def get_moves_by_a_star(self) -> None:
|
|
||||||
x, y = self.__tractor.get_position()
|
x, y = self.__tractor.get_position()
|
||||||
node = Node(None, x, y, self.__tractor.get_direction(), 0, "movement", "initial state")
|
if coords is not None:
|
||||||
board = copy.deepcopy(self.__board)
|
x1, y1 = coords
|
||||||
|
else:
|
||||||
|
x1, y1 = None, None
|
||||||
|
print(action, coords)
|
||||||
|
if action != A_DO_NOTHING and x1 == x and y == y1:
|
||||||
|
self.__tractor.do_action_on_fields(action)
|
||||||
|
elif action == A_DO_NOTHING:
|
||||||
|
print(action)
|
||||||
|
elif not self.__moves:
|
||||||
|
print(f"A Star is failed")
|
||||||
|
else:
|
||||||
|
print(f"A Star is succeed")
|
||||||
|
self.__bot_is_running.set()
|
||||||
|
self.__tractor.run_auto_bot_handler(action, self.__moves, self.__bot_is_running)
|
||||||
|
|
||||||
|
def get_coords_and_action(self) -> tuple[tuple[int, int], str]:
|
||||||
|
coords, action = self.__tractor.choose_action()
|
||||||
|
if coords is not None:
|
||||||
|
self.get_moves_by_a_star(coords)
|
||||||
|
else:
|
||||||
|
self.__moves = None
|
||||||
|
return coords, action[0]
|
||||||
|
|
||||||
|
def get_moves_by_a_star_with_random_coords(self) -> None:
|
||||||
x1 = random.randint(0, HORIZONTAL_NUM_OF_FIELDS)
|
x1 = random.randint(0, HORIZONTAL_NUM_OF_FIELDS)
|
||||||
y1 = random.randint(0, VERTICAL_NUM_OF_FIELDS)
|
y1 = random.randint(0, VERTICAL_NUM_OF_FIELDS)
|
||||||
dest = (x1, y1)
|
dest = (x1, y1)
|
||||||
self.__moves = AStar.search_solution(PriorityQueue(), Queue(), dest, node,
|
self.get_moves_by_a_star(dest)
|
||||||
|
|
||||||
|
def get_moves_by_a_star(self, coords: tuple[int, int]) -> None:
|
||||||
|
x, y = self.__tractor.get_position()
|
||||||
|
node = Node(None, x, y, self.__tractor.get_direction(), 0, "movement", "initial state")
|
||||||
|
board = copy.deepcopy(self.__board)
|
||||||
|
self.__moves = AStar.search_solution(PriorityQueue(), Queue(), coords, node,
|
||||||
lambda n=node, b=board: AStar.succ(n, b),
|
lambda n=node, b=board: AStar.succ(n, b),
|
||||||
lambda d=dest, n=node: AStar.goaltest_by_coords(d, n), board)
|
lambda d=coords, n=node: AStar.goaltest_by_coords(d, n), board)
|
||||||
|
|
||||||
def get_moves_by_bfs(self) -> None:
|
def get_moves_by_bfs(self) -> None:
|
||||||
x, y = self.__tractor.get_position()
|
x, y = self.__tractor.get_position()
|
||||||
@ -123,11 +157,14 @@ class App:
|
|||||||
|
|
||||||
while self.__running:
|
while self.__running:
|
||||||
self.__clock.tick(FPS)
|
self.__clock.tick(FPS)
|
||||||
|
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
self.event_handler(event)
|
self.event_handler(event)
|
||||||
|
|
||||||
if not self.__bot_is_running.is_set():
|
if not self.__bot_is_running.is_set() and not self.__auto_mode:
|
||||||
self.keys_pressed_handler()
|
self.keys_pressed_handler()
|
||||||
|
if not self.__bot_is_running.is_set() and self.__auto_mode:
|
||||||
|
self.auto_moving_mode()
|
||||||
|
|
||||||
self.loop_handler()
|
self.loop_handler()
|
||||||
self.update_screen()
|
self.update_screen()
|
||||||
|
@ -89,7 +89,7 @@ class AStar(Graphsearch):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def search_solution(fringe: PriorityQueue, explored: Queue, destination: tuple[int, int], istate: Node,
|
def search_solution(fringe: PriorityQueue, explored: Queue, destination: tuple[int, int], istate: Node,
|
||||||
succ: Callable[[Node, Board], list],
|
succ: Callable[[Node, Board], list],
|
||||||
goaltest: Callable[[tuple[int,int], Node], bool], board: Board) -> Union[bool, list]:
|
goaltest: Callable[[tuple[int, int], Node], bool], board: Board) -> Union[bool, list]:
|
||||||
|
|
||||||
print(f"Start A*")
|
print(f"Start A*")
|
||||||
fringe.put(PriorityItem(istate, 0))
|
fringe.put(PriorityItem(istate, 0))
|
||||||
|
@ -26,7 +26,9 @@ class CommandLineParser:
|
|||||||
if arg == SAVE_MAP:
|
if arg == SAVE_MAP:
|
||||||
print("Saving map")
|
print("Saving map")
|
||||||
results.append(Arg(SAVE_MAP))
|
results.append(Arg(SAVE_MAP))
|
||||||
|
if arg == AUTO_MODE:
|
||||||
|
print("Auto mode")
|
||||||
|
results.append(Arg(AUTO_MODE))
|
||||||
elif arg.find(LOAD_MAP, 0, len(LOAD_MAP)-1):
|
elif arg.find(LOAD_MAP, 0, len(LOAD_MAP)-1):
|
||||||
cmd = arg.split("=")
|
cmd = arg.split("=")
|
||||||
if len(cmd) == 2:
|
if len(cmd) == 2:
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
from tensorflow.keras.models import Sequential, save_model
|
|
||||||
from tensorflow.keras.layers import Dense, Flatten, Conv2D
|
|
||||||
from tensorflow.keras.losses import sparse_categorical_crossentropy
|
|
||||||
from tensorflow.keras.optimizers import Adam
|
|
||||||
from tensorflow.keras.preprocessing.image import ImageDataGenerator
|
|
||||||
from config import RESOURCE_DIR
|
|
||||||
import os
|
|
||||||
|
|
||||||
# labels
|
|
||||||
|
|
||||||
labels = ["cabbage", "carrot", "corn", "lettuce", "paprika", "potato", "tomato"]
|
|
||||||
|
|
||||||
# Data configuration
|
|
||||||
training_set_folder = os.path.join(RESOURCE_DIR, 'smaller_train')
|
|
||||||
test_set_folder = os.path.join(RESOURCE_DIR, 'smaller_test')
|
|
||||||
|
|
||||||
# Model config
|
|
||||||
batch_size = 25
|
|
||||||
img_width, img_height, img_num_channels = 25, 25, 3
|
|
||||||
loss_function = sparse_categorical_crossentropy
|
|
||||||
no_classes = 7
|
|
||||||
no_epochs = 40
|
|
||||||
optimizer = Adam()
|
|
||||||
verbosity = 1
|
|
||||||
|
|
||||||
# Determine shape of the data
|
|
||||||
input_shape = (img_width, img_height, img_num_channels)
|
|
||||||
|
|
||||||
# Create a generator
|
|
||||||
train_datagen = ImageDataGenerator(
|
|
||||||
rescale=1./255
|
|
||||||
)
|
|
||||||
train_datagen = train_datagen.flow_from_directory(
|
|
||||||
training_set_folder,
|
|
||||||
save_to_dir=os.path.join(RESOURCE_DIR, "adapted-images"),
|
|
||||||
save_format='jpeg',
|
|
||||||
batch_size=batch_size,
|
|
||||||
target_size=(25, 25),
|
|
||||||
class_mode='sparse')
|
|
||||||
|
|
||||||
# Create the model
|
|
||||||
model = Sequential()
|
|
||||||
model.add(Conv2D(16, kernel_size=(5, 5), activation='relu', input_shape=input_shape))
|
|
||||||
model.add(Conv2D(32, kernel_size=(5, 5), activation='relu'))
|
|
||||||
model.add(Conv2D(64, kernel_size=(5, 5), activation='relu'))
|
|
||||||
model.add(Conv2D(128, kernel_size=(5, 5), activation='relu'))
|
|
||||||
model.add(Flatten())
|
|
||||||
model.add(Dense(16, activation='relu'))
|
|
||||||
model.add(Dense(no_classes, activation='softmax'))
|
|
||||||
|
|
||||||
# Display a model summary
|
|
||||||
model.summary()
|
|
||||||
|
|
||||||
# Compile the model
|
|
||||||
model.compile(loss=loss_function,
|
|
||||||
optimizer=optimizer,
|
|
||||||
metrics=['accuracy'])
|
|
||||||
|
|
||||||
# Start training
|
|
||||||
model.fit(
|
|
||||||
train_datagen,
|
|
||||||
epochs=no_epochs,
|
|
||||||
shuffle=False)
|
|
||||||
|
|
||||||
# Saving model
|
|
||||||
filepath = os.path.join(RESOURCE_DIR, 'saved_model')
|
|
||||||
save_model(model, filepath)
|
|
||||||
|
|
@ -59,10 +59,10 @@ class Sand(Soil):
|
|||||||
self._value = VALUE_OF_SAND
|
self._value = VALUE_OF_SAND
|
||||||
|
|
||||||
def transform(self) -> list:
|
def transform(self) -> list:
|
||||||
if not self.is_hydrated :
|
if not self.is_sowed :
|
||||||
return [0, 1, 0, 0]
|
|
||||||
else:
|
|
||||||
return [0, 0, 1, 0]
|
return [0, 0, 1, 0]
|
||||||
|
else:
|
||||||
|
return [0, 1, 0, 0]
|
||||||
|
|
||||||
|
|
||||||
class Grass(Plant):
|
class Grass(Plant):
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
from tensorflow.keras.models import load_model
|
|
||||||
from tensorflow import keras as k
|
|
||||||
import numpy as np
|
|
||||||
from config import RESOURCE_DIR
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Load the model
|
|
||||||
model = load_model(
|
|
||||||
os.path.join(RESOURCE_DIR, "saved_model"),
|
|
||||||
custom_objects=None,
|
|
||||||
compile=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Data fror predictions
|
|
||||||
img_width, img_height, img_num_channels = 25, 25, 3
|
|
||||||
labels = ["cabbage", "carrot", "corn", "lettuce", "paprika", "potato", "tomato"]
|
|
||||||
|
|
||||||
# Predictions
|
|
||||||
filename = 'Image_1.jpg'
|
|
||||||
loaded_image = k.preprocessing.image.load_img(path=RESOURCE_DIR + '/smaller_test/potato/' + filename,
|
|
||||||
target_size=(img_width, img_height, img_num_channels))
|
|
||||||
# convert to array and resample dividing by 255
|
|
||||||
img_array = k.preprocessing.image.img_to_array(loaded_image) / 255.
|
|
||||||
|
|
||||||
# add sample dimension. the predictor is expecting (1, CHANNELS, IMG_WIDTH, IMG_HEIGHT)
|
|
||||||
img_np_array = np.expand_dims(img_array, axis=0)
|
|
||||||
|
|
||||||
predictions = model.predict(img_np_array)
|
|
||||||
prediction = np.argmax(predictions[0])
|
|
||||||
print(f'Ground truth: {filename} - Prediction: {labels[prediction]}')
|
|
@ -31,7 +31,7 @@ class Tractor(BaseField):
|
|||||||
self.__board = board
|
self.__board = board
|
||||||
self.__harvested_corps = []
|
self.__harvested_corps = []
|
||||||
self.__fuel = 10
|
self.__fuel = 10
|
||||||
self.__neural_network = None
|
self.__neural_network = NeuralNetwork()
|
||||||
self.__tree = DecisionTree()
|
self.__tree = DecisionTree()
|
||||||
self.__weather = Weather()
|
self.__weather = Weather()
|
||||||
|
|
||||||
@ -105,7 +105,10 @@ class Tractor(BaseField):
|
|||||||
field.is_sowed = True
|
field.is_sowed = True
|
||||||
|
|
||||||
def harvest(self) -> None:
|
def harvest(self) -> None:
|
||||||
if self.check_field(Crops):
|
field = self.get_field_from_board()
|
||||||
|
prediction = self.__neural_network.check(field)
|
||||||
|
|
||||||
|
if prediction.capitalize() in CROPS:
|
||||||
print('Harvest')
|
print('Harvest')
|
||||||
field = self.get_field_from_board()
|
field = self.get_field_from_board()
|
||||||
self.harvest_crops(field)
|
self.harvest_crops(field)
|
||||||
@ -194,26 +197,47 @@ class Tractor(BaseField):
|
|||||||
print(f"Length of Moves {len(moves)}") # - {3 ** len(moves)}")
|
print(f"Length of Moves {len(moves)}") # - {3 ** len(moves)}")
|
||||||
while len(moves) > 0:
|
while len(moves) > 0:
|
||||||
movement, action = moves.pop(0)
|
movement, action = moves.pop(0)
|
||||||
|
|
||||||
# do action
|
# do action
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
print(f"Action {action}")
|
self.do_action_on_fields(action)
|
||||||
if action == A_FERTILIZE:
|
|
||||||
self.fertilize()
|
|
||||||
elif action == A_SOW:
|
|
||||||
self.sow()
|
|
||||||
self.hydrate()
|
|
||||||
elif action == A_HYDRATE:
|
|
||||||
self.hydrate()
|
|
||||||
elif action == A_HARVEST:
|
|
||||||
self.harvest()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# move
|
# move
|
||||||
|
time.sleep(1)
|
||||||
self.move_or_rotate(movement)
|
self.move_or_rotate(movement)
|
||||||
|
|
||||||
time.sleep(TIME_OF_MOVING)
|
time.sleep(TIME_OF_MOVING)
|
||||||
is_running.clear()
|
is_running.clear()
|
||||||
|
|
||||||
|
def do_action_on_fields(self, action: str) -> None:
|
||||||
|
print(f"Action {action}")
|
||||||
|
if action == A_FERTILIZE:
|
||||||
|
self.fertilize()
|
||||||
|
elif action == A_SOW:
|
||||||
|
self.sow()
|
||||||
|
# self.hydrate()
|
||||||
|
elif action == A_HYDRATE:
|
||||||
|
self.hydrate()
|
||||||
|
elif action == A_HARVEST:
|
||||||
|
self.harvest()
|
||||||
|
|
||||||
|
def run_auto_bot_handler(self, action: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
|
||||||
|
thread = threading.Thread(target=self.run_auto_bot, args=(action, moves, is_running), daemon=True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def run_auto_bot(self, action_type: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
|
||||||
|
print(moves)
|
||||||
|
# print('Auto mode')
|
||||||
|
while len(moves) > 0:
|
||||||
|
movement, action = moves.pop(0)
|
||||||
|
# move
|
||||||
|
self.move_or_rotate(movement)
|
||||||
|
time.sleep(0.7)
|
||||||
|
|
||||||
|
self.do_action_on_fields(action_type)
|
||||||
|
time.sleep(1)
|
||||||
|
is_running.clear()
|
||||||
|
|
||||||
def move_or_rotate(self, movement: str) -> None:
|
def move_or_rotate(self, movement: str) -> None:
|
||||||
print(f"Move {movement}")
|
print(f"Move {movement}")
|
||||||
if movement == M_GO_FORWARD:
|
if movement == M_GO_FORWARD:
|
||||||
@ -309,16 +333,27 @@ class Tractor(BaseField):
|
|||||||
|
|
||||||
is_running.clear()
|
is_running.clear()
|
||||||
|
|
||||||
def choose_action(self) -> tuple[tuple[int,int],str]:
|
def choose_action(self) -> tuple[tuple[int, int], str]:
|
||||||
vectors = self.__board.convert_fields_to_vectors()
|
vectors = self.__board.convert_fields_to_vectors()
|
||||||
print(vectors)
|
print(vectors)
|
||||||
coords = None
|
coords = None
|
||||||
action = None
|
action = None
|
||||||
for i in range(HORIZONTAL_NUM_OF_FIELDS):
|
|
||||||
for j in range(VERTICAL_NUM_OF_FIELDS):
|
i_max = HORIZONTAL_NUM_OF_FIELDS
|
||||||
|
j_max = VERTICAL_NUM_OF_FIELDS
|
||||||
|
|
||||||
|
i_min = random.randint(0, HORIZONTAL_NUM_OF_FIELDS - 1)
|
||||||
|
j_min = random.randint(0, VERTICAL_NUM_OF_FIELDS - 1)
|
||||||
|
|
||||||
|
i = i_min
|
||||||
|
flag = True
|
||||||
|
while i < i_max and flag:
|
||||||
|
for j in range(j_min, j_max):
|
||||||
action = self.__tree.make_decision(self.__weather, vectors[i][j])
|
action = self.__tree.make_decision(self.__weather, vectors[i][j])
|
||||||
if action != A_DO_NOTHING:
|
if action != A_DO_NOTHING:
|
||||||
coords = (i, j)
|
coords = (i, j)
|
||||||
|
flag = False
|
||||||
break
|
break
|
||||||
|
i += 1
|
||||||
print(coords, action)
|
print(coords, action)
|
||||||
return coords, action
|
return coords, action
|
||||||
|
11
config.py
11
config.py
@ -14,7 +14,7 @@ __all__ = (
|
|||||||
'A_SOW', 'A_HARVEST', 'A_HYDRATE', 'A_FERTILIZE', 'A_DO_NOTHING',
|
'A_SOW', 'A_HARVEST', 'A_HYDRATE', 'A_FERTILIZE', 'A_DO_NOTHING',
|
||||||
'TYPES_OF_ACTION', 'D_NORTH', 'D_EAST', 'D_SOUTH', 'D_WEST',
|
'TYPES_OF_ACTION', 'D_NORTH', 'D_EAST', 'D_SOUTH', 'D_WEST',
|
||||||
'VALUE_OF_CROPS', 'VALUE_OF_PLANT', 'VALUE_OF_SAND', 'VALUE_OF_CLAY',
|
'VALUE_OF_CROPS', 'VALUE_OF_PLANT', 'VALUE_OF_SAND', 'VALUE_OF_CLAY',
|
||||||
'MAP_FILE_NAME', 'JSON', 'SAVE_MAP', 'LOAD_MAP',
|
'MAP_FILE_NAME', 'JSON', 'SAVE_MAP', 'LOAD_MAP', 'AUTO_MODE',
|
||||||
'TRAINING_SET_DIR', 'TEST_SET_DIR', 'ADAPTED_IMG_DIR', 'MODEL_DIR',
|
'TRAINING_SET_DIR', 'TEST_SET_DIR', 'ADAPTED_IMG_DIR', 'MODEL_DIR',
|
||||||
'DATA_DIR','IMG_DECISION_TREE','MODEL_TREE_FILENAME','DATA_TRAINING_FOR_DECISION_TREE'
|
'DATA_DIR','IMG_DECISION_TREE','MODEL_TREE_FILENAME','DATA_TRAINING_FOR_DECISION_TREE'
|
||||||
)
|
)
|
||||||
@ -86,10 +86,10 @@ A_DO_NOTHING = "do nothing"
|
|||||||
TYPES_OF_ACTION = [A_SOW, A_HARVEST, A_HYDRATE, A_FERTILIZE, A_DO_NOTHING]
|
TYPES_OF_ACTION = [A_SOW, A_HARVEST, A_HYDRATE, A_FERTILIZE, A_DO_NOTHING]
|
||||||
|
|
||||||
# Costs fields:
|
# Costs fields:
|
||||||
VALUE_OF_CROPS = 0.25
|
VALUE_OF_CROPS = 0.2
|
||||||
VALUE_OF_PLANT = 0.5
|
VALUE_OF_PLANT = 0.3
|
||||||
VALUE_OF_SAND = 0.75
|
VALUE_OF_SAND = 0.4
|
||||||
VALUE_OF_CLAY = 1
|
VALUE_OF_CLAY = 0.5
|
||||||
|
|
||||||
# Weather
|
# Weather
|
||||||
W_SUNNY = 'Sunny'
|
W_SUNNY = 'Sunny'
|
||||||
@ -113,3 +113,4 @@ TIME_OF_MOVING = 2
|
|||||||
# Args
|
# Args
|
||||||
SAVE_MAP = '--save-map'
|
SAVE_MAP = '--save-map'
|
||||||
LOAD_MAP = '--load-map'
|
LOAD_MAP = '--load-map'
|
||||||
|
AUTO_MODE = '--auto-mode'
|
||||||
|
Loading…
Reference in New Issue
Block a user