Skip to content

Instantly share code, notes, and snippets.

@zischwartz
Created March 23, 2018 12:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zischwartz/a302e37175922d44c605a281a36236b8 to your computer and use it in GitHub Desktop.
Save zischwartz/a302e37175922d44c605a281a36236b8 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
body {
background-color: black;
height: 100vh;
overflow: hidden;
}
#app {
margin-left: auto;
margin-right: auto;
width: 800px;
height: 800px;
/* margin-top: 0px; */
/* background-color: black; */
}
svg {
background-color: rgb(40,40,40);
width: 800px;
height: 800px;
}
svg text {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
svg text::selection {
background: none;
}
svg text {
fill: white;
}
rect {
fill: none;
/* fill: black; */
}
#score {
color: rgb(250,250,250);
text-align: right;
font-family: sans-serif;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- For offline dev -->
<!-- <script src="d3.v4.min.js"></script> -->
<script src="tinyboard.js"></script>
<!-- <script src="game.js"></script> -->
<script src="mgame.js"></script>
<!-- <script src="game_examples.js"></script> -->
<script>
// XXX YOU PROBABLY DON'T NEED TO EDIT THIS FILE
// Go edit game.js instead
let wrap = d3.select( document.getElementById("app") )
let score_el = wrap.append('div').attr('id', 'score')
let svg = wrap.append('svg')
let sel = svg.append('g').attr('id', 'game')
function render_board(data){
let board = Board()
.tile_size(800/10)
sel.datum(data).call(board)
}
function set_score(score){
score_el.html(score)
}
let game = new Game(render_board, set_score)
// register our event listener
document.addEventListener('keydown', (event) => {
if (game.keypressed){
game.keypressed(event.key)
}
// console.log(event.key)
})
// call game's do_every_second
if (game.do_every_second){
setInterval(game.do_every_second.bind(game), 1000)
}
</script>
</html>
class Game {
constructor(render_board, set_score){
this.render_board = render_board
this.set_score = set_score
this.score = 0
this.board_size = 10 // 10 rows by 10 cols
// we use this to count the total number item's we've added, to make sure they
// have unique ids
this.all_objects_ever_count = 0
this.set_score(this.score)
this.data = []
// just add one money item to start, for easier debug. was 10 in class
this.add_item('💰', 1)
this.data.push({row:2, col: 5, icon:'🐄', id:'player' })
this.add_item('👿', 1)
this.add_item('🌲', 3)
this.render_board(this.data)
}
add_item(icon, number_to_add){
for (var i = 0; i < number_to_add; i++) {
let row = Math.floor(Math.random()*this.board_size)
let col = Math.floor(Math.random()*this.board_size)
// in class I used this.data.length for the item id here, but I realized that's
// a mistake and will result in duplicate ids IF we're removing items from this.data (which we are)
this.data.push({row, col, icon, id:`${this.all_objects_ever_count}`})
// increment all_objects_ever_count so the next money id will be unique
this.all_objects_ever_count+=1
}
}
keypressed(keyname){
let player = this.get_by_id('player')
if (keyname == 'ArrowUp'){
player['row'] = player['row']-1
}
else if (keyname == 'ArrowDown'){
player['row'] +=1
}
else if (keyname == 'ArrowLeft'){
player['col'] -=1
}
else if (keyname == 'ArrowRight'){
player['col'] +=1
}
this.process_money(player)
this.process_enemies(player)
this.render_board(this.data)
}
// move enemies and collision detection with player
process_enemies(player){
let all_enemies = this.data.filter( function(x){ return x.icon == '👿'})
for (var i = 0; i < all_enemies.length; i++) {
let enemy = all_enemies[i]
// randomly 1 or 0
let should_move = Math.round(Math.random())
// if should_move is true, move this enemy towards the player,
// along the x axis (col number)
if (enemy.col > player.col && should_move){
enemy.col-=1
} else if (enemy.col < player.col && should_move){
enemy.col+=1
}
// did the enemy touch a player?
if (enemy.col == player.col && enemy.row == player.row){
this.score -=1
// turn the player into a hamburger
player['icon'] = '🍔'
this.set_score(this.score)
}
}
}
// collision detection for player hitting a money item
process_money(player){
let all_money = this.data.filter( function(x){ return x.icon == '💰'})
for (var i = 0; i < all_money.length; i++) {
let money = all_money[i]
if (money.col == player.col && money.row == player.row){
this.score +=1
this.set_score(this.score)
this.data = this.data.filter( function(x){ return x.id !=money.id})
this.add_item('💰',2)
this.add_item('👿',1)
}
}
}
// helper
get_by_id(id){
return this.data.find( x=> x.id==id)
}
// do_every_second() automatically gets called once a second in index.html
do_every_second(){
// you can use it to move things around without user interaction
// change this.data and uncomment this line
// this.render_board(this.data)
}
}
// XXX YOU ARE NOT MEANT TO EDIT THIS FILE
// XXX YOU ARE NOT MEANT TO EDIT THIS FILE
// XXX YOU ARE NOT MEANT TO EDIT THIS FILE
let key_func_by_id = function(d) {
return d.id
}
let is_data_invalid = function(data){
let have_ids = data.filter( (x) => typeof x.id != 'undefined' )
if (have_ids.length >0 && have_ids.length != data.length){
return true
}
else {
return false
}
}
// https://bost.ocks.org/mike/chart/
// XXX meant to be used with datum(), not data()
// i.e. selection should just be a single g
function Board() {
// All options that should be accessible to caller
let tile_size = 30
let margin = 1
let click = () => {}
function board(selection) {
selection.each(function(data, i) {
if (is_data_invalid(data)){
throw Error('Doh! Your data is not consistent - It looks like some of your items have the `id` property and some do not')
}
let spaces
if (data.filter( (x) => typeof x.id != 'undefined' ).length){
spaces = d3.select(this).selectAll(".space").data(data, key_func_by_id)
} else {
spaces = d3.select(this).selectAll(".space").data(data)
}
let spacesEnter = spaces
.enter()
.append("g")
.attr("class", "space")
spacesEnter.append("rect")
spacesEnter.append("text")
spacesEnter.attr(
"transform",
(d, i) => `translate(${d.col * tile_size}, ${d.row * tile_size})`
)
// enter+update
let spacesMerge = spacesEnter.merge(spaces)
spaces.exit().remove()
spacesMerge
.transition()
// .duration(400)
.attr(
"transform",
(d, i) => `translate(${d.col * tile_size}, ${d.row * tile_size})`
)
.select("rect")
.attr("width", tile_size - margin)
.attr("height", tile_size - margin)
spacesMerge
.select("text")
// .attr("font-size", 13)
.attr("font-size", 48)
.text(d => {
return d.icon
})
// .attr("y", ".3em")
.attr("y", (tile_size - margin) / 1.5)
.attr("x", (tile_size - margin) / 2)
.attr("text-anchor", "middle")
.attr("text-align", "center")
spacesMerge.on("click", click)
// console.log(cluesMerge)
}) // end selection.each
} // end board function we return
board.tile_size = function(value) {
if (!arguments.length) return tile_size
tile_size = value
// if (typeof updateWidth === "function") updateWidth()
return board
}
board.margin = function(value) {
if (!arguments.length) return margin
margin = value
// if (typeof updateHeight === "function") updateHeight()
return board
}
board.click = function(value) {
if (!arguments.length) return click
click = value
return board
}
return board
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment