BASIC-RoBots / src / robot / robot.py

##
## robot.py for BASIC-RoBots
## 
## Copyright (C) 2012 Pierre Surply
## <pierre.surply@gmail.com>
##
## This file is part of BASIC-RoBots.
##
##    BASIC-RoBots is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    BASIC-RoBots is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with BASIC-RoBots.  If not, see <http://www.gnu.org/licenses/>.
## 
## Started on  Sun Jul  1 15:34:10 2012 Pierre Surply
## Last update Tue Sep  4 11:04:18 2012 Pierre Surply
##

import os
import shutil
import random
from collections import deque
from pygame.locals import *

import robotsos
import basic
import inventory
import pathfinding

class Robot:
    def __init__(self, new, world, name, env, (x, y), events):
        self.env = env
        self.pos_x = x
        self.pos_y = y
        self.pos_z = float(random.randint(0, 200)) / 1000
        self.d_pos_z = 0.002
        self.real_pos_x = 0.0
        self.real_pos_y = 0.0
        self.angle = 0
        self.orient = 0
        self.id_sprite = 0
        self.name = name
        self.world = world
        self.events = events
        self.cannot_move = [2, 4, 6]
        self.energy = 0xFF
        self.shield = 0xFF
        self.keys = 0
        self.inv = inventory.Inventory("saves/"+self.world+"/robots/"+self.name+"/inv", 10000)
        if new:
            self.new()
            self.save_pos()
        else:
            self.load_pos()
            self.load_prop()
            self.load_inventory()
        self.mem = {"A" : 0,\
                        "B" : 0,\
                        "C" : 0,\
                        "D" : 0,\
                        "O" : 0}
        self.mem_stack = []
        self.msg_recv = deque([])
        self.ext_cmd = {"forward" : (self.forward, \
                                         "Move the robot forward", \
                                         "1 if the robot can move, 0 otherwise",\
                                         (0, []),\
                                         {}),\
                            "rotleft": (self.rot_left, \
                                            "Rotate the robot counterclockwise",\
                                            "Always returns 0", \
                                            (0, []),\
                                            {}),\
                            "rotright": (self.rot_right, \
                                             "Rotate the robot clockwise",\
                                             "Always returns 0", \
                                             (0, []),\
                                             {}),\
                            "give": (self.give, \
                                         "Give an item from inventory to other robot",\
                                         "1 if the item is given, 0 otherwise",\
                                         (0, []),\
                                         {"A" : "item id"}),\
                            "sendmsg": (self.sendmsg, \
                                         "Send a message (1 byte) to all robots in a small area",\
                                         "Number of robots which received the message",\
                                         (0, []),\
                                         {"A" : "message"}),\
                            "recvmsg": (self.recvmsg, \
                                            "Read a message sent by other robot",\
                                            "Content of the message if received, 0 otherwise ",\
                                            (0, []),\
                                            {}), \
                            "findpath": (self.findpath, \
                                             "",\
                                             "",\
                                             (0, []),\
                                             {}), \
                            "finditem": (self.radar_item, \
                                             "",\
                                             "",\
                                             (0, []),\
                                             {})}
        self.os = robotsos.RoBotsOS(self, "saves/"+world+"/robots/"+self.name+"/fs", \
                                        self.mem, \
                                        events)

    def new(self):
        i = 0
        new_name = self.name + "_" + str(i)
        while os.path.exists("saves/"+self.world+"/robots/"+new_name):
            i += 1
            new_name = self.name + "_" + str(i)
        self.name = new_name
        os.makedirs("saves/"+self.world+"/robots/"+self.name+"/fs")
        self.inv.path = "saves/"+self.world+"/robots/"+self.name+"/inv"
        shutil.copytree("saves/"+self.world+"/lib", \
                            "saves/"+self.world+"/robots/"+self.name+"/fs/lib")

    def get_pos(self):
        return (self.pos_x, self.pos_y)

    def get_mem_ro(self):
        return {"SH" : self.shield,\
                    "EN" : self.energy,\
                    "OR" : self.orient,\
                    "X" : self.pos_x,\
                    "Y" : self.pos_y, \
                    "K" : self.keys}

    def update(self, selected, display, events):
        if selected:
            self.handle_events(events)
        self.pos_z += self.d_pos_z
        if self.pos_z > 0.2:
            self.d_pos_z = -0.002
        elif self.pos_z < 0:
            self.d_pos_z = 0.002
        th_angle = 90 * self.orient
        if (self.angle%360 != th_angle):
            if (th_angle-self.angle)%360 < 180:
                self.angle += 10
            else:
                self.angle -= 10
        if self.pos_x - 0.1 > self.real_pos_x:
            self.real_pos_x += 0.1
        elif self.pos_x + 0.1 < self.real_pos_x:
            self.real_pos_x -= 0.1
        if self.pos_y - 0.1 > self.real_pos_y:
            self.real_pos_y += 0.1
        elif self.pos_y + 0.1 < self.real_pos_y:
            self.real_pos_y -= 0.1
        self.os.update(selected, display, events)

    def handle_events(self, events):
        if events.key[K_LCTRL]:
            if events.key[K_LALT] and \
                    events.get_key_once(K_r):
                self.os = robotsos.RoBotsOS(self, "saves/"+self.world+"/robots/"+self.name+"/fs", \
                                            self.mem, \
                                            events)
            if self.os.task == 1 and events.get_key_once(K_c):
                self.os.basic.interrupt(basic.KeyInterrupt)

        if self.os.task == 1:
            keys = self.keys
            self.keys = int(events.key[K_1]) | \
                int(events.key[K_2]) << 1 | \
                int(events.key[K_3]) << 2 | \
                int(events.key[K_4]) << 3 | \
                int(events.key[K_5]) << 4 | \
                int(events.key[K_6]) << 5 | \
                int(events.key[K_7]) << 6 | \
                int(events.key[K_8]) << 7
            if keys < self.keys:
                self.os.basic.interrupt(basic.KeyPressed)

    def save_pos(self):
        f = open("saves/"+self.world+"/robots/"+self.name+"/pos", 'w')
        buf = str(self.pos_x) + "-" + str(self.pos_y) + "-" + str(self.orient)
        f.write(buf)
        f.close()

    def save_prop(self):
        f = open("saves/"+self.world+"/robots/"+self.name+"/prop", 'w')
        buf = str(self.energy)
        f.write(buf)
        f.close()
        
    def save(self):
        self.save_prop()
        self.save_pos()
        self.inv.save()

    def load_pos(self):
        if os.path.isfile("saves/"+self.world+"/robots/"+self.name+"/pos"):
            f = open("saves/"+self.world+"/robots/"+self.name+"/pos", 'r')
            buf = f.read().split("-")
            f.close()
            self.pos_x = int(buf[0])
            self.pos_y = int(buf[1])
            self.real_pos_x = float(self.pos_x)
            self.real_pos_y = float(self.pos_y)
            self.orient = int(buf[2])
            self.angle = 90*self.orient

    def load_prop(self):
        if os.path.isfile("saves/"+self.world+"/robots/"+self.name+"/prop"):
            f = open("saves/"+self.world+"/robots/"+self.name+"/prop", 'r')
            self.energy = int(f.read())

    def load_inventory(self):
        if os.path.isfile("saves/"+self.world+"/robots/"+self.name+"/inv"):
            f = open("saves/"+self.world+"/robots/"+self.name+"/inv", 'r')
            self.inv.parse(f.read())
            f.close()

    def coll(self, (x, y)):
        return not(x >= 0 and \
                    y >= 0 and \
                    x < self.env.size and\
                    y < self.env.size and\
                    self.env.tile_elts[(y*self.env.size)+x] == 0xFF and\
                    not self.env.tile[(y*self.env.size)+x] in self.cannot_move and\
                    not (x, y) in self.env.get_pos_robots())

    def run_ext_cmd(self, f, price):
        if self.energy > 0:
            result = f()
            if result != 0:
                self.incr_energy(-price[0])
            return result
        else:
            self.os.terminal.info.write_info("Not enough energy", 8)

    def incr_energy(self, incr):
        self.energy += incr
        if self.energy <= 0:
            self.energy = 0
            self.os.terminal.write("*** Out of energy *** \nStopping robot...\n", 6)
        elif self.energy < 50 and incr > 0:
            self.os.terminal.write("Warning : the energy level is low\n", 5)
        elif self.energy > 255:
            self.energy = 255
        
    def move_to(self, (x, y)):
        if not self.coll((x, y)):
            self.pos_x = x
            self.pos_y = y
            self.save_pos()
            self.os.terminal.info.write_info("Moved to (" + str(x) + ", " + str(y) + ")", 7)
            self.env.calculate_light()
            return 1
        else:
            self.os.terminal.info.write_info("Can't move to (" + str(x) + ", " + str(y) + ")", 8)
            return 0

    def get_pos_forward(self):
        if self.orient == 0:
            return (self.pos_x, self.pos_y-1)
        elif self.orient == 1:
            return (self.pos_x+1, self.pos_y)
        elif self.orient == 2:
            return (self.pos_x, self.pos_y+1)
        elif self.orient == 3:
            return (self.pos_x-1, self.pos_y)

    def forward(self):
        if self.inv.isfull():
            self.os.terminal.info.write_info("The robot carries too much", 8)
            return 0
        else:
            return self.move_to(self.get_pos_forward())

    def rot_left(self):
        return self.rot(1)

    def rot_right(self):
        return self.rot(-1)
        
    def rot(self, incr):
        self.orient += incr
        if self.orient < 0:
            self.orient = 3
        elif self.orient > 3:
            self.orient = 0
        return 0

    def add_item(self, item, qty):
        if qty > 0 and item < len(self.inv.temp_items):
            temp = self.inv.temp_items[item]
            self.inv.set_item(item, qty)
            self.os.terminal.info.write_info("[INV] " + temp[0] + " collected (" + \
                                            str(qty) + "x" + str(temp[1]) + "g)", 1)
            if self.inv.isfull():
                self.os.terminal.info.write_info("Warning : The robot carries too much", 8)

    def add_item_rnd(self, item, qty, prob):
        if (random.randint(1, prob) == 1):
            self.add_item(item, random.randint(1, qty))

    def del_item(self, item, qty):
        if qty > 0 and item < len(self.inv.temp_items):
            temp = self.inv.temp_items[item]
            if self.inv.set_item(item, -qty):
                self.os.terminal.info.write_info("[INV] " + temp[0] + " removed (" + \
                                                str(qty) + "x" + str(temp[1]) + "g)", 1)
                return True
            else:
                return False
        else:
            return False

    def give(self):
        id_item = self.mem["A"]
        qty = 1
        pos = self.get_pos_forward()
        for i in self.env.robots:
            if i.get_pos() == pos and self.del_item(id_item, qty):
                i.add_item(id_item, qty)
                return 1
        return 0

    def sendmsg(self):
        msg = self.mem["A"]
        nbr_robots = 0
        for i in self.env.robots:
            if (i.pos_x != self.pos_x  or i.pos_y != self.pos_y):
                i.msg_recv.append(msg)
                nbr_robots += 1
        return nbr_robots

    def recvmsg(self):
        if len(self.msg_recv) > 0:
            return self.msg_recv.popleft()
        else:
            return 0

    def findpath(self):
        if 0 <= self.mem["A"] < self.env.size and \
                0 <= self.mem["B"] < self.env.size:
            path = pathfinding.findpath(self.coll, \
                                            (self.env.size, self.env.size), \
                                            (self.pos_x, self.pos_y), \
                                            (self.mem["A"], self.mem["B"]), \
                                            1 << 8)
            if path == None:
                return 0
            else:
                path_or = []
                current_pos = path[0]
                for i in path[1:]:
                    if i[1] < current_pos[1]:
                        ori = 0
                    elif i[0] > current_pos[0]:
                        ori = 1
                    elif i[1] > current_pos[1]:
                        ori = 2
                    elif i[0] < current_pos[0]:
                        ori = 3
                    path_or.append(ori)
                    current_pos = i
                path_or.append(5)
                path_or.reverse()
                self.mem_stack += path_or
                return 1
        else:
            return 0


    def radar_item(self):
        item = self.mem["A"]
        nearest = None
        dist = None
        for y in range(self.env.size):
            for x in range(self.env.size):
                if self.env.tile_elts[x][y] == item and \
                        (nearest == None or dist > (x-self.pos_x)**2 + (y-self.pos_y)**2):
                    nearest = (x, y)
                    dist = (x-self.pos_x)**2 + (y-self.pos_y)**2
        if nearest is not None:
            self.mem_stack.append(nearest[1])
            self.mem_stack.append(nearest[0])
            return 1
        else:
            return 0
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.