/location-based-scavenger-hunt

A Python/CGI framework for creating your own customizable location-based collaborative scavenger hunts

Primary LanguageJavaScriptGNU General Public License v2.0GPL-2.0

Location-Based Scavenger Hunt

A Python/CGI framework for creating your own customizable location-based collaborative scavenger hunts. Works on any mobile device with a modern browser, such as iPhone and Android. No app installation required.

For Christmas 2015, I decided I'd like to gift my family an experience instead of material gifts. There are a few services out there that provide location-based scavenger hunts, but none (that I could find) are free or open-source. So, I created my own. And now, this is my gift to you.

Sample questions included are for a Christmas-based hunt in Cincinnati, but can easily be repurposed for your own uses (for any occasion, in any city).

Features:

  • Multi-user, collaborative game. Everyone competes collaboratively, but against each other for the final prize.
    • Number of users limited only by resources on game server (i.e. virtually unlimited)
    • Teams may also compete against each other (one login per team)
  • Define your own questions (in order)
  • Answers may be text-based, location-based, or both
  • Each question may have an arbitrary number of hints (opening a new hint will cost a user some points)
  • Several Easter eggs / plot-twists
  • "Gullible challenges" between users (one user must convince another of an obviously incorrect answer)
  • Admin panel to modify score, current question, disabled questions

Techy stuff:

  • On client side, browser-based gameplay. Works on any mobile device with a modern browser (Chrome recommended). No app installation required.
  • Server side runs a Python script in CGI environment (TLS recommended)
  • Session management. Each client may login from any IP (as is required as mobile phones hop around the city throughout the challenge). Cookie-based sessions ensure that a specific user is logged in from at most 1 browser.

Screenshots

Sample question:

A correct answer:

An incorrect answer:

A question linking to a geocache:

A location-based question:

A sample hint:

Embedded HTML, in this case a YouTube video:

A question including an interactive HTML5 Simon game:

Admin panel:

Setup

Server Setup

To run a scavenger hunt, you will need a web server under your control.

Following is one recommended configuration:

Regardless of your web server, you'll need a few Python goodies available on the server:

  • pip for Python package management
    • sudo apt-get install python-pip
  • dill as alternative for Python's pickle
    • sudo pip install dill
  • geopy for geolocation in Python
    • sudo pip install geopy

Game Setup

Following should be set up per-game:

  • Fill out game_config.py with your game parameters
    • USERS table (in a list of "(username, login hash)" tuples)
    • ADMIN_USERS list
    • Name of game arch-nemesis (e.g. "The Grinch")
    • UTC time that game is expected to end
    • Define questions. Refer to the Question class for more info. Sample questions included are for a Christmas-based hunt in Cincinnati, but can easily be repurposed for your own uses (for any occasion, in any city).
  • If using the (included) Simon question, be sure to update required end score and URL on line 114 of simon_says.js. The q=14 must match the question number in game_config.py.
  • Set up a login method. Login URL = https://your-server-url.here:55864/cgi-bin/game.py?l=hash (where hash comes from the USERS list in game_config.py). Methods:
    • Email a personalized login link to every user (no sharing login links). This is the preferred method (see Client Setup below).
    • Print a QR code for each user using the personalized login link

For ideas on devising creative questions:

  • Use the included Simon game
  • Hide a clue inside a real geocache, and then link to the geocache as part of a question
  • Use Google Voice for a custom voicemail greeting revealing a required clue
  • Use Twilio to do neat things with text messages
  • Use IFTTT for some neat conditional clues

Client Setup

If you're using QR codes for login, users may need to download a barcode reader, such as QR Reader for iPhone or Barcode Scanner for Android. Further, you may not be able to control what browser opens after having scanned a QR code, which may be problematic (e.g. Safari on iPhone has been observed to be wonky).

Chrome is the recommended mobile browser, both on iPhone and Android. For that reason, perhaps emailing a personalized login link to every user is the preferred method.

When a client logs in the first time, he/she will have to grant two privileges:

  • Accept the self-signed certificate (unless you have a CA-signed one). Usually, it's Advanced --> Proceed.
  • Allow Chrome to detect/use your location

If you don't accept/allow both of these, then you'll have a sad time. A nerd should supervise the process of initial logins.

Ensure that everyone reads the instructions (on Question 1) before anyone attempts to answer Question 1.

Gameplay

Questions are served, one at a time, in order, beginning with question #1 (which also provides some basic instructions). All users playing the game always see the same question. When one user answers a question correctly, all users see the next question. Automatic refreshes are intentionally omitted to keep from frustrating users while mid-input. There is a refresh link that can/should be used frequently to see the latest game updates. All users will be able to see the current scoreboard.

Some questions require a correct text answer. Others require a user to be in the "right" location. Other questions have both constraints.

When the last question is correctly answered, the game is over ... maybe. For more details, see the Spoiler wiki page.

Admin Interface

Each game can have any number of administrators, who do not participate but ensure that the game continues without any problems and on schedule. From the admin panel, an admin has the ability to:

  • Manually edit current scores (form at the top)
  • Set the current question (radio buttons in the Cur column)
  • Disable (future) questions (to expedite a long game) (checkboxes in the Dis column)
  • Reset the game (see Resetting a Game below)

An admin could use a browser on a phone, tablet, or even computer. No need for location services.

Resetting a Game

To start a brand new game, or in case of catastrophic failure, admins have the ability to delete all persistent game state. This can be done via the admin panel, or by accessing the following URL directly:

  • https://your-server-url.here:55864/cgi-bin/reset.py?l=hash (where hash comes from an admin in the USERS list in game_config.py)

Non-admins cannot reset games.

After a game reset, all players (including admins) must close all tabs and re-login.

Sample lighttpd Config

server.modules = (
        "mod_access",
        "mod_alias",
        "mod_compress",
        "mod_redirect",
        "mod_cgi",
)

server.document-root        = "/var/www"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80

index-file.names            = ( "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".py", ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
## Use ipv6 if available
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

$HTTP["url"] =~ "^/cgi-bin/" {
    cgi.assign = (".py" => "/usr/bin/python")
}

$SERVER["socket"] == ":55864" {
  ssl.engine = "enable" 
  ssl.pemfile = "/etc/lighttpd/certs/lighttpd.pem" 
}

ssl.honor-cipher-order = "enable"
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
ssl.use-compression = "disable"
setenv.add-response-header = (
    "Strict-Transport-Security" => "max-age=63072000; includeSubdomains; preload",
    "X-Frame-Options" => "DENY",
    "X-Content-Type-Options" => "nosniff"
)
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"

Licenses

Software I wrote is governed by the GNU v2 license.

For included software (not written by me):