Browse Source

added wind, fuel, etc.

master
Will Webberley 8 years ago
parent
commit
51301e8560
  1. 5
      README.md
  2. 48
      cardiff_drone.py
  3. 3
      manager.py
  4. 29
      maps.json
  5. 167
      static/css/main.css
  6. BIN
      static/media/altitude.png
  7. BIN
      static/media/compass.png
  8. BIN
      static/media/droplet.png
  9. BIN
      static/media/home.png
  10. BIN
      static/media/inventory.png
  11. BIN
      static/media/logout.png
  12. BIN
      static/media/map.jpeg
  13. BIN
      static/media/map.png
  14. BIN
      static/media/pin.png
  15. BIN
      static/media/play.png
  16. BIN
      static/media/swords.png
  17. BIN
      static/media/swords2.png
  18. BIN
      static/media/swords3.png
  19. BIN
      static/media/test.png
  20. BIN
      static/media/user.png
  21. 5
      templates/dashboard.html
  22. 31
      templates/level.html

5
README.md

@ -145,9 +145,10 @@ Maps can be managed directly through the `maps.json` file. Each map is rectangul
- `obstacles` - `[[x,y,z]]`: a list of 3D coordinate lists. e.g. `"obstacles": [[1,1,0],[1,1,1]]` creates two obstacles at x=1,y=1 at different heights.
- `finish` - `[x,y,z]`: the 3D (x,y,z) coordinate of the finish square.
- `start` - `[x,y,z,a]`: the 3D coordinate of the start sqaure and the initial direction the Drone faces (90,180,270,0).
- `fuel` - `int`: the amount of fuel the Drone is initially given.
- `size` - `[w,h]`: the width and height of the map.
- `ammo` - `int`: The initial ammo held by the drone (currently un-used but necessary for construction).
- `items` - `[{item }]`: List of item objects. Each item should have a `name` (string) and `location` ([x,y,z]) attribute.
- `ammo` - `int`: the initial ammo held by the drone (currently un-used but necessary for construction).
- `items` - `[{item }]`: List of item objects. Each item should have a `name` (string) and `location` ([x,y,z]) attribute. Fuel is a valid collection item, and should be given a name like `fuel (x)`, where `x` is an integer representing the amount of fuel to be replenished upon collection.
- `finish_items` - `[str]`: List of item names required to complete the level. All of the names in this array must be present in the `items` array.
Note that the Drone will crash if it leaves the map's bounds or if it flies higher than z=9. Therefore, keep relevant items and the finish and start squares to a lower height.

48
cardiff_drone.py

@ -7,11 +7,13 @@ class Drone:
self.y = self.map['start'][1]
self.z = self.map['start'][2]
self.a = self.map['start'][3]
self.fuel = self.map['fuel']
self.ammo = self.map['ammo']
self.inventory = []
self.crashed = False
self.finished = False
self.status = "Ready"
self.status = "Ready"
self.moves = 0
self.positions = [(self.x,self.y,self.z,self.a,self.status,self.inventory)]
def __str__(self):
@ -39,7 +41,21 @@ class Drone:
if i not in self.inventory:
self.finished = False
break
if self.finished == False:
if self.fuel == 0:
self.z = 0
self.crashed = True
if self.moves % self.map['wind']['frequency'] == 0:
if self.map['wind']['towards'] == 'N':
self.y-=1
if self.map['wind']['towards'] == 'E':
self.x+=1
if self.map['wind']['towards'] == 'S':
self.y+=1
if self.map['wind']['towards'] == 'W':
self.x-=1
if self.finished == True:
self.status = "Finished"
elif self.crashed == True:
@ -52,32 +68,46 @@ class Drone:
new_invent = []
for i in self.inventory:
new_invent.append(i)
self.positions.append((self.x,self.y,self.z,self.a,self.status,new_invent))
self.positions.append((self.x,self.y,self.z,self.a,self.status,new_invent,self.fuel))
def pick_up(self):
for i in self.map['items']:
if i['location'][0] == self.x and i['location'][1] == self.y and i['location'][2] == self.z:
self.inventory.append(i['name'])
if i['name'].startswith('fuel ('):
f = int(i['name'].split(" ")[1].replace(")","").replace("(",""))
print "refuelling: "+str(f)
self.fuel+=f
else:
self.inventory.append(i['name'])
self.map['items'].remove(i)
self.moves+=1
def takeoff(self):
if self.crashed == False and self.finished == False:
self.z = 2
self.fuel -= 2
self.moves+=1
self.get_state()
def land(self):
if self.crashed == False and self.finished == False:
self.z = 0
self.fuel -= 1
self.moves+=1
self.get_state()
def move_up(self):
if self.can_move():
self.z += 1
self.fuel -= 1
self.moves+=1
self.get_state()
def move_down(self):
if self.can_move():
self.z -= 1
self.fuel -= 1
self.moves+=1
self.get_state()
def turn_right(self):
@ -86,6 +116,8 @@ class Drone:
if self.a >= 360:
diff = self.a-diff
self.a = diff
self.fuel -= 1
self.moves+=1
self.get_state()
def turn_left(self):
@ -94,6 +126,8 @@ class Drone:
if self.a < 0:
diff = 0-self.a
self.a = diff
self.fuel -= 1
self.moves+=1
self.get_state()
def move_forward(self):
@ -106,6 +140,8 @@ class Drone:
self.y+=1
if self.a == 270:
self.x-=1
self.fuel -= 1
self.moves+=1
self.get_state()
def move_backward(self):
@ -118,6 +154,8 @@ class Drone:
self.y-=1
if self.a == 270:
self.x+=1
self.fuel -= 1
self.moves+=1
self.get_state()
def move_right(self):
@ -130,6 +168,8 @@ class Drone:
self.x-=1
if self.a == 270:
self.y-=1
self.fuel -= 1
self.moves+=1
self.get_state()
def move_left(self):
@ -142,4 +182,6 @@ class Drone:
self.x+=1
if self.a == 270:
self.y+=1
self.fuel -= 1
self.moves+=1
self.get_state()

3
manager.py

@ -88,7 +88,8 @@ def run_drone(map_number):
return_obj['result'] = moves
return_obj['error'] = False
return json.dumps(return_obj)
except:
except Exception as e:
print e
return_obj['error'] = True
return json.dumps(return_obj)

29
maps.json

@ -1,10 +1,13 @@
[
{
"name": "Introduction",
"obstacles": [[3,5,0],[3,5,1],[3,5,2]],
"description": "Move the drone to the finish point to complete the level.",
"obstacles": [],
"finish": [6,3,0],
"start": [4,3,0,90],
"size": [10,10],
"fuel": 8,
"wind": { "towards": "N", "frequency": 1},
"ammo": 0,
"finish_items": [],
"items": [
@ -12,8 +15,11 @@
},
{
"name": "Obstacles!",
"description": "Avoid the obstacles on the way to the finish point to complete the level.",
"obstacles": [[5,9,0],[5,9,1],[5,9,2],[5,9,3],[5,9,4],[12,7,1],[12,7,2]],
"finish": [12,9,0],
"fuel": 30,
"wind": { "towards": "S", "frequency": 3},
"start": [1,1,0,180],
"size": [20,20],
"ammo": 0,
@ -22,9 +28,12 @@
]
},
{
"name": "Recovery",
"name": "Recovery Position",
"description": "Recover an item before moving to the finish.",
"obstacles": [[6,3,2],[6,3,3],[6,3,4],[6,3,5],[6,3,6],[6,3,7]],
"finish": [2,19,0],
"fuel": 35,
"wind": { "towards": "N", "frequency": 100},
"start": [13,3,0,0],
"size": [20,20],
"ammo": 0,
@ -33,5 +42,21 @@
{ "name": "crate", "location": [9,8,0] },
{ "name": "crashed UAV", "location": [2,11,0] }
]
},
{
"name": "Frugal Droning",
"description": "Complete the mission without running out of fuel.",
"obstacles": [[6,2,2],[9,2,4],[12,2,4],[13,2,5],[15,2,1],[18,2,1],[18,2,3],[19,8,2],[19,10,1],[19,12,4],[19,13,2],[19,15,2],[19,18,4],[2,5,3],[2,6,4],[2,10,1],[2,19,3],[4,19,2],[5,19,1],[5,19,3],[9,19,2],[10,19,4],[13,19,1],[15,19,3],[15,19,4],[15,19,5],[15,19,6],[17,19,2],[18,18,1],[18,18,2],[18,18,3],[18,18,4],[18,18,5],[18,18,6],[18,18,7],[18,18,8],[17,20,1],[17,20,2],[17,20,3],[17,20,4],[17,20,5],[17,20,6],[17,20,7],[17,20,8],[3,3,1],[4,3,2],[5,3,3],[5,3,4],[5,4,2]],
"finish": [19,19,0],
"fuel": 35,
"wind": { "towards": "N", "frequency": 100},
"start": [2,2,0,180],
"size": [20,20],
"ammo": 0,
"finish_items": [],
"items": [
{ "name": "fuel (7)", "location": [17,2,0] },
{ "name": "fuel (10)", "location": [10,19,0] }
]
}
]

167
static/css/main.css

@ -42,32 +42,6 @@ body{
background-position:center center;
}
input{
border: 1px dotted rgba(0,0,0,0.7);
padding: 5px;
margin:10px 0px;
border-radius:2px;
font-size:15px;
background-size:20px;
background-position: 2px center;
background-repeat:no-repeat;
width:80%;
}
input[type=password]{
background-image: url('/static/media/key.png');
padding-left: 25px;
}
input[type=email]{
background-image: url('/static/media/email.png');
padding-left:25px;
background-size:15px;
}
input.username{
background-image: url('/static/media/user.png');
padding-left:25px;
background-size:15px;
}
textarea.drone_source{
display:block;
margin:0px;
@ -82,61 +56,32 @@ textarea.drone_source{
button{
border:none;
font-weight:100;
background: #d4374a;
background: rgb(100,100,100);
background: rgb(0,0,50);
padding: 5px;
padding: 10px;
margin: 10px 0px;
color: white;
font-size: 16px;
text-transform:uppercase;
border-radius:4px;
letter-spacing:1px;
border-radius:2px;
border-bottom:2px solid rgba(0,0,0,0.6);
}
button:hover{
opacity:0.7;
}
button.battle_play{
width:100%;
display:block;
}
button.battle_step{
width:46%;
margin:0px 1%;
padding:5px 1%;
display:inline-block;
}
button.delete{
padding-left:30px;
background-image: url('/static/media/bin.png');
background-repeat:no-repeat;
background-size:18px;
background-position:4px center;
background-color:rgb(150,0,0);
}
button.add{
padding-left:30px;
background-image: url('/static/media/add.png');
background-repeat:no-repeat;
background-size:18px;
background-position:4px center;
}
button.source{
button.run{
padding-left:30px;
background-image: url('/static/media/curlybrace.png');
background-repeat:no-repeat;
background-size:18px;
background-position:4px center;
background-color:rgb(0,100,0);
}
button.test{
padding-left:30px;
background-image: url('/static/media/test.png');
background-repeat:no-repeat;
background-size:18px;
background-position:4px center;
}
button.battle{
button.play{
padding-left:30px;
background-image: url('/static/media/swords2.png');
background-image: url('/static/media/play.png');
background-repeat:no-repeat;
background-size:18px;
background-position:4px center;
@ -170,16 +115,21 @@ h2.battle{
p{
font-weight:200;
}
.info{
background-image:url('/static/media/info.png');
background-size:20px;
p.info{
background-size:auto 20px;
background-repeat:no-repeat;
background-position:4px 4px;
background-position:4px center;
padding:5px;
padding-left:30px;
background-color:rgba(100,100,100, 0.3);
border-bottom:4px solid rgba(100,100,100,0.4);
}
min-height: 25px;
line-height:25px;
}
p.info.fuel{background-image:url('/static/media/droplet.png');}
p.info.position{background-image:url('/static/media/pin.png');}
p.info.altitude{background-image:url('/static/media/altitude.png');}
p.info.bearing{background-image:url('/static/media/compass.png');}
p.info.inventory{background-image:url('/static/media/inventory.png');}
p.error{
background-image:url('/static/media/error.png');
background-size:20px;
@ -235,41 +185,6 @@ header #cs_text span.robotathon{
font-size:25px;
font-family: 'Share Tech Mono' ;
}
nav{
margin:0px auto;
display:inline-block;
vertical-align:middle;
height:50px;
text-align:right;
margin-right:10%;
}
nav a:link{
height:50px;
width:40px;
display: inline-block;
margin:0px 10px;
color: rgba(0,0,0,0.4);
text-decoration:none;
border-radius:3px 3px 0px 0px;
background-position:center center;
background-size:36px;
background-repeat:no-repeat;
}
nav a.logout{
background-image:url('/static/media/logout.png');
background-color:rgba(0,0,0,0.15);
}
nav a.dashboard{
background-image:url('/static/media/home.png');
}
nav a.battle{
background-image:url('/static/media/swords3.png');
background-size:25px;
}
nav a:hover{
background-color:rgba(0,0,0,0.1);
}
section{
width: 80%;
@ -342,46 +257,6 @@ ul.robot_list li .score{
right:5px;
}
.vs_box{
display:block;
}
.vs_box .robot{
margin:0px;
padding:0px;
background-size:100px;
background-repeat:no-repeat;
background-position:10px center;
background-image:url('/static/media/robot.png');
min-height:100px;
display:block;
margin:20px auto;
padding:10px;
padding-left:130px;
background-color:rgba(0,0,0,0.1);
}
.vs_box .robot .title{
font-family: 'Share Tech Mono' ;
font-size:25px;
margin:0px;
}
.vs_box .robot .owner{
margin:0px;
}
.vs_box .robot .score{
margin-top:10px;
display:block;
text-align:right;
font-size:60px;
font-weight:300;
margin:0px;
color:red;
}
.vs_box .robot .score.player2{
color:blue;
}
#map{
display:block;
margin:20px auto;

BIN
static/media/altitude.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
static/media/email.png → static/media/compass.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
static/media/add.png → static/media/droplet.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
static/media/home.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

BIN
static/media/bin.png → static/media/inventory.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
static/media/logout.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

BIN
static/media/map.jpeg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

BIN
static/media/map.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/media/info.png → static/media/pin.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
static/media/key.png → static/media/play.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
static/media/swords.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
static/media/swords2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
static/media/swords3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
static/media/test.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

BIN
static/media/user.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

5
templates/dashboard.html

@ -13,7 +13,7 @@
{% for level in levels %}
<li>
<p class="title">{{ level['name'] }}</p>
<button onclick="window.location='/level/{{ level['id'] }}';">Play</button>
<button onclick="window.location='/level/{{ level['id'] }}';" class="play">Play</button>
</li>
{% endfor %}
</ul>
@ -22,6 +22,7 @@
<div class="half">
<h2>Completed levels</h2>
<p>As you complete levels, they will show below.</p>
{% if num_completed_levels == 0 %}
<p class="error">You haven't completed a level yet!</p>
{% else %}
@ -30,7 +31,7 @@
<li>
<p class="title">{{ level['map_name'] }}</p>
<p class="subtitle">Lines of code: {{ level['code_lines'] }}</p>
<button onclick="window.location='/level/{{ level['map_id'] }}';">Replay</button>
<button onclick="window.location='/level/{{ level['map_id'] }}';" class="play">Replay</button>
</li>
{% endfor %}
</ul>

31
templates/level.html

@ -18,10 +18,11 @@
<div id="status"></div> <p id="status_label"></p>
<div class="clear"></div>
<hr />
<p>Position: (<span id="current_position"></span>)</p>
<p>Altitude: <span id="current_altitude"></span></p>
<p>Bearing: <span id="current_bearing"></span></p>
<p>Inventory:</p>
<p class="info position">(<span id="current_position"></span>)</p>
<p class="info altitude"><span id="current_altitude"></span></p>
<p class="info fuel"><span id="current_fuel"></span></p>
<p class="info bearing"><span id="current_bearing"></span></p>
<p class="info inventory"></p>
<ul id="current_inventory"></ul>
</div>
</div>
@ -35,14 +36,17 @@ drone = Drone(maps[{{level['id']-1}}])
# Enter your code here
</textarea>
<button id="run">Run code</button>
<button id="run" class="run">Run code</button>
</div>
<div class="half">
<h2>Mission information</h2>
<p id="map_desc"></p>
<h3>Starting position</h3>
<p>The drone starts at position (<span id="drone_start"></span>) facing <span id="drone_start_angle"></span>.</p>
<h3>Finish position</h3>
<p>Get the drone to position (<span id="drone_finish"></span>) to finish.
<h3>Wind</h3>
<p>Wind will blow to <span id="wind_direction"></span> every <span id="wind_frequency"></span> Drone move(s).
<div id="available_items_info">
<h3>Items available</h3>
<p>The following items can be collected:</p>
@ -60,13 +64,14 @@ drone = Drone(maps[{{level['id']-1}}])
{% endblock %}
{% block scripts %}
<script>
var obstacles, items, finish_items, finish_tile, start_tile, map_size, drone, tile_width, tile_height, selected_cell;
var obstacles, items, finish_items, finish_tile, start_tile, map_size, drone, tile_width, tile_height, selected_cell, map_desc, wind;
function Drone(x,y,z,a){
function Drone(x,y,z,a,fuel){
this.x = x;
this.y = y;
this.z = z;
this.a = a;
this.fuel = fuel;
this.inventory = [];
this.status = "green";
this.status_label = "Landed";
@ -159,6 +164,10 @@ drone = Drone(maps[{{level['id']-1}}])
$("#map .tile").click(function(){
analyse_cell($(this));
});
$("#map_desc").html(map_desc);
$("#wind_direction").html(wind.towards);
$("#wind_frequency").html(wind.frequency);
}
function draw_drone(){
@ -172,12 +181,14 @@ drone = Drone(maps[{{level['id']-1}}])
$("#altimeter .current_level").animate({'top':(9*(9-drone.z))+'%'},500, "easeOutBounce");
$("#current_position").html(drone.x+','+drone.y);
$("#current_fuel").html(drone.fuel);
$("#current_altitude").html(drone.z);
$("#current_bearing").html(drone.a+'&deg; from North');
$("#current_inventory").html('');
for(var i = 0; i < drone.inventory.length; i++){
$("#current_inventory").append('<li>'+drone.inventory[i]+'</li>');
}
if(drone.inventory.length == 0){$("#current_inventory").html('Empty');}
$("#status").removeClass("green").removeClass("red").addClass(drone.status);
$("#status_label").html(drone.status_label);
@ -200,7 +211,9 @@ drone = Drone(maps[{{level['id']-1}}])
finish_tile = data.finish;
start_tile = data.start;
map_size = data.size;
drone = new Drone(start_tile[0],start_tile[1],start_tile[2],start_tile[3]);
map_desc = data.description;
wind = data.wind;
drone = new Drone(start_tile[0],start_tile[1],start_tile[2],start_tile[3],data.fuel);
draw_ui();
});
}
@ -213,7 +226,6 @@ drone = Drone(maps[{{level['id']-1}}])
postdata.code = $(".drone_source").val();
$.post("/run_drone/"+map_id, postdata, function(data){
data = JSON.parse(data);
console.log(data);
if(data.error == true){
$("#run").fadeIn();
alert("Oops - there is an error in your Python code. Have a check through.");
@ -235,6 +247,7 @@ drone = Drone(maps[{{level['id']-1}}])
drone.y = move[1];
drone.z = move[2];
drone.a = move[3];
drone.fuel = move[6];
drone.status_label = move[4];
if(move[4] == "Crashed"){
drone.status = "red";

Loading…
Cancel
Save