diff --git a/.atom-build.yml b/.atom-build.yml new file mode 100644 index 0000000..0a677b2 --- /dev/null +++ b/.atom-build.yml @@ -0,0 +1 @@ +cmd: python3 problems/maze/run_game.py diff --git a/.gitignore b/.gitignore index 4bfbc46..627f19e 100755 --- a/.gitignore +++ b/.gitignore @@ -271,3 +271,5 @@ nohub.out gitpull.php nycsl.ini + +*.gch diff --git a/problems/maze/env/main.py b/problems/maze/env/main.py new file mode 100644 index 0000000..ae7a220 --- /dev/null +++ b/problems/maze/env/main.py @@ -0,0 +1,27 @@ +from flask import Flask, request +import json +import random + +games = {} + +app = Flask(__name__) + +@app.route('/start') +def start(): + key = ''.join(random.choice('0123456789ABCDEF') for i in range(16)) + games[key] = Map(21, 21, random.randint(0, 4294967295)) + return key + +@app.route('/move/') +def move(key, move): + result = games[key].make_move(move) + if result != 0: + send_string("Result: " + ("SUCCESS" if result == 1 else "FAILURE") + " in " + str(time.time()-games[key].start_time) + " seconds.") + del games[key] + return time.time()-games[key].start_time if result == 1 else -1 # -1 indicates failure; any other value is the time it took them to complete the maze. + games[key].update_visibility() + games[key].delay() + send_string(games[key].serialize()) # Josh, I'm assuming that 'send_string' exists, because I have no idea how you want it to be written. + +if __name__ == '__main__': + app.run(host='0.0.0.0',port=7500,debug=True) diff --git a/problems/maze/env/maze.py b/problems/maze/env/maze.py new file mode 100644 index 0000000..6793703 --- /dev/null +++ b/problems/maze/env/maze.py @@ -0,0 +1,151 @@ +import socket, json, collections, time, sys + +# Directions +NORTH = 0 +EAST = 1 +SOUTH = 2 +WEST = 3 + +# Tile states +UNKNOWN = 0 +EMPTY = 1 # transparent +WALL = 2 # opaque +GLASS = 3 # transparent +GOAL = 4 # transparent +PLAYER = 5 + +# Max amount of time for player to respond. +MAX_SEC = 60 +MIN_DELAY = 0.025 + +# The following class and functions are for testing visibility. +class Point: + def __init__(self, x = 0, y = 0): + self.x = x + self.y = y + def __repr__(self): + return '(' + str(self.x) + ', ' + str(self.y) + ')' + def __eq__(self, other): + return self.x == other.x and self.y == other.y + def __key(self): + return (self.x, self.y) + def __hash__(self): + return hash(self.__key()) +def get_orientation(p1, p2, p3): + val = (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y) + if val == 0: return 0 + if val > 0: return 1 + return -1 +def col_is_on_seg(p1, p2, test): + return test.x >= min(p1.x, p2.x) and test.x <= max(p1.x, p2.x) and test.y >= min(p1.y, p2.y) and test.y <= max(p1.y, p2.y) +def does_intersect(l1, l2): + o1 = get_orientation(l1[0], l1[1], l2[0]) + o2 = get_orientation(l1[0], l1[1], l2[1]) + o3 = get_orientation(l2[0], l2[1], l1[0]) + o4 = get_orientation(l2[0], l2[1], l1[1]) + if o1 != o2 and o3 != o4: return True + if o1 == 0 and col_is_on_seg(l1[0], l1[1], l2[0]): return True + if o2 == 0 and col_is_on_seg(l1[0], l1[1], l2[1]): return True + if o3 == 0 and col_is_on_seg(l2[0], l2[1], l1[0]): return True + if o4 == 0 and col_is_on_seg(l2[0], l2[1], l1[1]): return True + return False + +class Maze: + def __init__(self, width, height, seed): + self.height = height + self.width = width + self.contents = [] + self.visibility = [] + for y in range(0, self.height): + contents_row = [] + visibility_row = [] + for x in range(0, self.width): + contents_row.append(EMPTY) # Full of empty squares to start. + visibility_row.append(False) + self.contents.append(contents_row) + self.visibility.append(visibility_row) + self.contents[0][0] = PLAYER + self.player_loc = (0, 0) + self.opaque_walls = [] # We'll use these for finding what walls are opaque. + # We'll also assume that every square is 1x1, as it doesn't matter. + self.contents[2][2] = WALL # for testing + self.contents[2][3] = WALL # for testing + for y in range(0, self.height): + for x in range(0, self.width): + if self.contents[y][x] == WALL: + self.opaque_walls.append((Point(x, y), Point(x, y+1))) + self.opaque_walls.append((Point(x, y), Point(x+1, y))) + self.opaque_walls.append((Point(x+1, y), Point(x+1, y+1))) + self.opaque_walls.append((Point(x, y+1), Point(x+1, y+1))) + self.opaque_walls = set(self.opaque_walls) + self.start_time = time.time() + def make_move(self, dir): + self.contents[self.player_loc[1]][self.player_loc[0]] = EMPTY + if dir == NORTH and self.player_loc[1] != 0 and (self.contents[self.player_loc[1]-1][self.player_loc[0]] == EMPTY or self.contents[self.player_loc[1]-1][self.player_loc[0]] == GOAL): + self.player_loc = (self.player_loc[0], self.player_loc[1]-1) + elif dir == EAST and self.player_loc[0] != self.width-1 and (self.contents[self.player_loc[1]][self.player_loc[0]+1] == EMPTY or self.contents[self.player_loc[1]][self.player_loc[0]+1] == GOAL): + self.player_loc = (self.player_loc[0]+1, self.player_loc[1]) + elif dir == SOUTH and self.player_loc[1] != self.height-1 and (self.contents[self.player_loc[1]+1][self.player_loc[0]] == EMPTY or self.contents[self.player_loc[1]+1][self.player_loc[0]] == GOAL): + self.player_loc = (self.player_loc[0], self.player_loc[1]+1) + elif dir == WEST and self.player_loc[0] != 0 and (self.contents[self.player_loc[1]][self.player_loc[0]-1] == EMPTY or self.contents[self.player_loc[1]][self.player_loc[0]-1] == GOAL): + self.player_loc = (self.player_loc[0]-1, self.player_loc[1]) + if self.contents[self.player_loc[1]][self.player_loc[0]] == GOAL: return 1 + self.contents[self.player_loc[1]][self.player_loc[0]] = PLAYER + return 0 if time.time() - self.start_time < MAX_SEC else -1 + def _get_neighbors(self, loc): + neighbors = [] + if loc[1] != 0: neighbors.append((loc[0], loc[1]-1)) + if loc[0] != self.width-1: neighbors.append((loc[0]+1, loc[1])) + if loc[1] != self.height-1: neighbors.append((loc[0], loc[1]+1)) + if loc[0] != 0: neighbors.append((loc[0]-1, loc[1])) + return neighbors + def _test_visible(self, loc): + corners = [ Point(loc[0], loc[1]),Point(loc[0]+1, loc[1]),Point(loc[0], loc[1]+1),Point(loc[0]+1, loc[1]+1) ] + center = Point(self.player_loc[0]+0.5, self.player_loc[1]+0.5) + for p in corners: + is_good = True + for wall in self.opaque_walls: + if wall[0] == p or wall[1] == p: + continue + if does_intersect(wall, (p, center)): + is_good = False + break + if is_good: + return True + return False + def update_visibility(self): + # Ensure that the squares directly adjacent to the player are visible. + for loc in self.get_neighbors(self.player_loc): + self.visibility[loc[1]][loc[0]] = True + bfs = collections.deque() + for y in range(0, self.height): + for x in range(0, self.width): + if self.visibility[y][x]: + bfs.appendleft((x, y)) + while len(bfs) > 0: + # We're going to use an additional value, -1, to mark tiles we've visited and confirmed are invisible this turn. + loc = bfs.pop() + for n in self.get_neighbors(loc): + if self.visibility[n[1]][n[0]] == False: + if self.test_visible(n): + self.visibility[n[1]][n[0]] = True + bfs.appendleft(n) + else: + self.visibility[n[1]][n[0]] = -1 + for y in range(0, self.height): + for x in range(0, self.width): + if self.visibility[y][x] == -1: + self.visibility[y][x] = False + def delay(self): + time.sleep(MIN_DELAY) + def serialize(self): + grid = [] + for y in range(0, self.height): + row = [] + for x in range(0, self.width): + if self.visibility[y][x]: + row.append(self.contents[y][x]) + else: + row.append(UNKNOWN) + grid.append(row) + return json.dumps(grid) diff --git a/problems/maze/run_game.py b/problems/maze/run_game.py deleted file mode 100644 index 2bea701..0000000 --- a/problems/maze/run_game.py +++ /dev/null @@ -1,30 +0,0 @@ -import socket, json - -UNKNOWN = 0 -EMPTY = 1 -WALL = 2 -GOAL = 3 -PLAYER = 4 - -class Map: - def __init__(self, width, height, seed): - self.contents = [] - for y in range(0, height): - row = [] - for x in range(0, width): - row.append((EMPTY, False)) # Full of empty yet unseen squares. - self.contents.append(row) - def serialize(self): - grid = [] - for y in range(0, len(self.contents)): - row = [] - for x in range(0, len(self.contents[0])): - if self.contents[y][x][1]: - row.append(self.contents[y][x][0]) - else: - row.append(UNKNOWN) - grid.append(row) - return json.dumps(grid) - -m = Map(5, 5, 0) -print(m.serialize()) diff --git a/problems/maze/starter/c++11/main.cpp b/problems/maze/starter/c++11/main.cpp new file mode 100644 index 0000000..cbcff09 --- /dev/null +++ b/problems/maze/starter/c++11/main.cpp @@ -0,0 +1,11 @@ +#include //Needed to get the time so that we can seed our random number generator. +#include "maze.hpp" //Contains the core maze utils, such as consts, typedefs, and the >> overload. + +int main() { + srand(time(NULL)); //Seeds random number generator with the time so that output is always different. + Maze m; + while(true) { + std::cin >> m; //Gets the present maze. + std::cout << rand() % 4; //Sends a random direction back. Directions are ints as defined in maze.hpp + } +} diff --git a/problems/maze/starter/c++11/maze.hpp b/problems/maze/starter/c++11/maze.hpp new file mode 100644 index 0000000..4cdd919 --- /dev/null +++ b/problems/maze/starter/c++11/maze.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include + +const int NORTH = 0; +const int EAST = 1; +const int SOUTH = 2; +const int WEST = 3; + +const int UNKNOWN = 0; +const int EMPTY = 1; +const int WALL = 2; +const int GLASS = 3; +const int GOAL = 4; +const int PLAYER = 5; + +typedef std::vector< std::vector > Maze; +static std::istream & operator>>(std::istream & i, Maze & m) { + std::string str; + char c; + do { + c = i.get(); + if(!isspace(c)) throw 0; + } while(c != '['); + int sq_br_cntr = 1, vrt_cntr = 0; + do { + c = i.get(); + if(c == '[') { + sq_br_cntr++; + vrt_cntr++; + str.push_back(' '); + } + else if(c == ']') { + sq_br_cntr--; + str.push_back(' '); + } + else if(c == ',') str.push_back(' '); + else if(isdigit(c)) str.push_back(c); + else if(!isspace(c)) throw 0; + } while(sq_br_cntr > 0); + m.resize(vrt_cntr); + int wdth = std::count(str.begin(), str.end(), ' ') / vrt_cntr; + std::stringstream strstrm(str); + for(auto a = m.begin(); a != m.end(); a++) { + a->resize(wdth); + for(auto b = a->begin(); b != a->end(); b++) strstrm >> *b; + } + return i; +} diff --git a/problems/maze/starter/c++11/Maze.cpp b/problems/maze/starter/java7/main.java similarity index 100% rename from problems/maze/starter/c++11/Maze.cpp rename to problems/maze/starter/java7/main.java diff --git a/problems/maze/starter/c++11/Networking.hpp b/problems/maze/starter/java7/maze.java similarity index 100% rename from problems/maze/starter/c++11/Networking.hpp rename to problems/maze/starter/java7/maze.java diff --git a/problems/maze/starter/python3/Maze.py b/problems/maze/starter/python3/Maze.py deleted file mode 100644 index e69de29..0000000 diff --git a/problems/maze/starter/python3/Networking.py b/problems/maze/starter/python3/Networking.py deleted file mode 100644 index e69de29..0000000 diff --git a/problems/maze/starter/java7/Maze.java b/problems/maze/starter/python3/main.py similarity index 100% rename from problems/maze/starter/java7/Maze.java rename to problems/maze/starter/python3/main.py diff --git a/problems/maze/starter/java7/Networking.java b/problems/maze/starter/python3/maze.py similarity index 100% rename from problems/maze/starter/java7/Networking.java rename to problems/maze/starter/python3/maze.py diff --git a/problems/maze/starter/run.py b/problems/maze/starter/run.py index e69de29..a02118a 100644 --- a/problems/maze/starter/run.py +++ b/problems/maze/starter/run.py @@ -0,0 +1,12 @@ +import subprocess, sys + +connect_to_server(); # Assumed to exist. + +proc = subprocess.Popen(sys.argv[1], stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True) + +while True: + s = get_string() # Assume to exist. + if s.split(' ')[0] == "Result:": + print(s) + break + proc.communicate(s)