Skip to content
This repository has been archived by the owner on Aug 4, 2018. It is now read-only.

Chaos, Random Pack, Autopick, Suggest Lands, Makefile, add SOI #153

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6afbf3e
Syntax update update
arxanas May 10, 2016
0f4ca3d
Add highlight and autopick text to hovered and autopicked cards
arxanas May 10, 2016
d17eebe
Implement autopicking
arxanas May 11, 2016
bc7bcd4
Merge pull request #1 from arxanas/autopick
dev-id May 11, 2016
e50f0e5
Implement basic connection status indicator
arxanas May 14, 2016
278088d
Add connection status indicator icon
arxanas May 14, 2016
ead8996
Correct Makefile dependencies to be parallelizable
arxanas May 14, 2016
d04e364
Implement automatic land suggestion
arxanas May 15, 2016
0b6a290
Merge branch 'autopick' into devel
arxanas May 15, 2016
b2ef0ed
Merge branch 'connection-monitor' into devel
arxanas May 15, 2016
1b6144a
Merge branch 'connection-monitor' into live
arxanas May 16, 2016
f0c4dd2
Merge branch 'suggest-lands' into live
arxanas May 16, 2016
a5df279
Require confirmation from each player before starting
arxanas May 16, 2016
d34cda7
Reactivate kick functionality
arxanas May 16, 2016
0b92a9c
Merge branch 'kick' into live
arxanas May 16, 2016
e67a7f6
Merge pull request #3 from arxanas/suggest-lands
dev-id May 17, 2016
2f39ff9
Add Chaos, Random Packs, SOI
dev-id May 17, 2016
605836d
fix Chaos title / cube error
dev-id May 23, 2016
5e3c9fd
Sort lands separate from 0cmc nonlands
dev-id May 24, 2016
7fbed59
Merge branch 'tritoch/chaos' into live
arxanas May 16, 2016
6c06293
Branding: change for drafts.ninja
arxanas May 26, 2016
57a1a17
Add logging for number of games
arxanas May 17, 2016
c1a0f59
Log the number of connected users
arxanas May 17, 2016
c4d432c
Add timestamp to logs
arxanas May 17, 2016
e2d91c2
Display the number of games in progress
arxanas May 18, 2016
af72a19
Display the number of connected users
arxanas May 18, 2016
ab4795b
Branding: update README with drafts.ninja features
arxanas May 26, 2016
544c219
Merge branch 'master' of https://github.com/arxanas/draft into arxana…
dev-id May 27, 2016
91272b9
EMA
dev-id May 31, 2016
caddb87
EMA
dev-id May 31, 2016
848c84e
Adjustable pick timer
dev-id Jun 3, 2016
8ba82e2
remove commented
dev-id Jun 3, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.PHONY: all install clean cards score js
all: install clean cards score js

node := ${CURDIR}/node_modules
all_sets := ${CURDIR}/data/AllSets.json
traceur := ${node}/.bin/traceur

${traceur}: install

install:
npm install
Expand All @@ -14,22 +19,22 @@ install:
ln -sf ${node}/utils/utils.js public/lib

clean:
rm -f data/AllSets.json
rm -f ${all_sets}

cards: data/AllSets.json
cards: ${all_sets}
node src/make cards

custom:
node src/make custom

data/AllSets.json:
curl -so data/AllSets.json https://mtgjson.com/json/AllSets.json
${all_sets}:
curl -so ${all_sets} https://mtgjson.com/json/AllSets.json

score:
-node src/make score #ignore errors

js:
node_modules/.bin/traceur --out public/lib/app.js public/src/init.js
js: ${traceur} ${all_sets}
${traceur} --out public/lib/app.js public/src/init.js

run: js
node run
53 changes: 37 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,49 @@
# draft
# drafts.ninja

unaffiliated with wizards of the coast
[drafts.ninja](http://drafts.ninja) is a fork of aeosynth's `draft` project. It
supports all of the features of `draft` and more. Here are some of the
highlights:

# run
* **Pick confirmation**: In order to prevent misclicks, `draft` requires you to
click a card twice in order to select it. However, the selected card is
indistinguishable from the other cards in the pack. The UI in drafts.ninja
indicates which card is currently selected.

- [node.js](http://nodejs.org/)
* **Autopick**: If your time expires, `draft` will select a card for you at
random. This rarely turns out well. If you have preliminarily selected a
card but not confirmed it, drafts.ninja will automatically pick it for you.

- `make`
* **Connection indicators**: Are your draftmates disconnected or just slow?
drafts.ninja displays a connection indicator next to each player in the
draft, letting you know if a player is no longer with us.

- `node app.js`
* **Kick players**: If one of your players has disconnected and is holding up
the draft, you can kick them and the rest of their picks will be made
automatically for them. No more abandoning the draft halfway through!

- <http://localhost:1337>
* **Ready confirmation**: Each player must mark themself as ready before the
game can start. If you have unresponsive players, you can kick them before
the draft has started and get a new person.

# updating
* **Suggest lands**: After agonizing over your maindeck, you don't want to
spend a lot of time constructing your manabase. With the click of a button,
drafts.ninja will add lands to your deck using an algorithm designed to
conservatively choose your color ratio. It'll even add some basic lands to your
sideboard as well, just in case.

generally you can update with `git pull`; if that doesn't work,
rerun `make`; if that still doesn't work, please file an issue
Like `draft` before it, drafts.ninja is unaffiliated with Wizards of the Coast,
and is licensed under the MIT license.

# etc
Bugs or feature requests? Feel free to open an issue.

written in [es6], transpiled with [traceur], using [react] on the client
# Installation

for the editor component, see <https://github.com/aeosynth/editor>
drafts.ninja is a NodeJS application. Install NodeJS, then just run `make run`
in your terminal and visit [http://localhost:1337](http://localhost:1337).

[es6]: https://github.com/lukehoban/es6features
[traceur]: https://github.com/google/traceur-compiler
[react]: https://github.com/facebook/react
drafts.ninja is written in [ES6] and transpiled with [Traceur], and uses [React]
on the client-side.

[ES6]: https://github.com/lukehoban/es6features
[Traceur]: https://github.com/google/traceur-compiler
[React]: https://github.com/facebook/react
3 changes: 2 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ var server = http.createServer(function(req, res) {
}).listen(PORT)
var eioServer = eio(server).on('connection', router)

console.log(new Date)
require('log-timestamp')
console.log('Started up')
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ee": "git://github.com/aeosynth/ee",
"engine.io": "^1.4.1",
"engine.io-client": "^1.4.1",
"log-timestamp": "^0.1.2",
"node-fetch": "^1.0.3",
"send": "^0.11.1",
"traceur": "0.0.65",
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>draft</title>
<title>drafts.ninja</title>
<link rel="stylesheet" href="lib/normalize.css">
<!-- https://github.com/driftyco/ionicons/issues/129 -->
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/1.5.2/css/ionicons.min.css">
Expand Down
20 changes: 12 additions & 8 deletions public/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,32 @@ let App = {

state: {
id: null,
name: 'newfriend',
name: 'ninja',

numPlayers: 0,
numActiveGames: 0,

seats: 8,
type: 'draft',
sets: [
'BFZ',
'BFZ',
'BFZ',
'BFZ',
'BFZ',
'BFZ'
'SOI',
'SOI',
'SOI',
'SOI',
'SOI',
'SOI'
],
list: '',
cards: 15,
packs: 3,

bots: true,
timer: true,
timer: 40,

beep: false,
chat: true,
cols: false,
deckSize: 40,
filename: 'filename',
filetype: 'txt',
side: false,
Expand Down
129 changes: 126 additions & 3 deletions public/src/cards.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ let Cards = {
}

export let BASICS = Object.keys(Cards)
let COLORS_TO_LANDS = {
'W': 'Plains',
'U': 'Island',
'B': 'Swamp',
'R': 'Mountain',
'G': 'Forest',
}

for (let name in Cards)
Cards[name] = {name,
Expand Down Expand Up @@ -82,6 +89,9 @@ let events = {
_.download(data, filename + '.' + filetype)
hash()
},
readyToStart(e) {
App.send('readyToStart', e.target.checked)
},
start() {
let {bots, timer} = App.state
let options = [bots, timer]
Expand Down Expand Up @@ -140,6 +150,109 @@ let events = {
delete Zones[zoneName][cardName]
App.update()
},
deckSize(e) {
let n = Number(e.target.value)
if (n && n > 0)
App.state.deckSize = n
App.update()
},
suggestLands() {
// Algorithm: count the number of mana symbols appearing in the costs of
// the cards in the pool, then assign lands roughly commensurately.
let colors = ['W', 'U', 'B', 'R', 'G']
let colorRegex = /\{[^}]+\}/g
let manaSymbols = {}
colors.forEach(x => manaSymbols[x] = 0)

// Count the number of mana symbols of each type.
for (let card of Object.keys(Zones['main'])) {
let quantity = Zones['main'][card]
card = Cards[card]

if (!card.manaCost)
continue
let cardManaSymbols = card.manaCost.match(colorRegex)

for (let color of colors)
for (let symbol of cardManaSymbols)
// Test to see if '{U}' contains 'U'. This also handles things like
// '{G/U}' triggering both 'G' and 'U'.
if (symbol.indexOf(color) !== -1)
manaSymbols[color] += quantity
}

_resetLands()
// NB: We could set only the sideboard lands of the colors we are using to
// 5, but this reveals information to the opponent on Cockatrice (and
// possibly other clients) since it tells the opponent the sideboard size.
colors.forEach(color => Zones['side'][COLORS_TO_LANDS[color]] = 5)

colors = colors.filter(x => manaSymbols[x] > 0)
colors.forEach(x => manaSymbols[x] = Math.max(3, manaSymbols[x]))
colors.sort((a, b) => manaSymbols[b] - manaSymbols[a])

// Round-robin choose the lands to go into the deck. For example, if the
// mana symbol counts are W: 2, U: 2, B: 1, cycle through the sequence
// [Plains, Island, Swamp, Plains, Island] infinitely until the deck is
// finished.
//
// This has a few nice effects:
//
// * Colors with greater mana symbol counts get more lands.
//
// * When in a typical two color deck adding 17 lands, the 9/8 split will
// be in favor of the color with slightly more mana symbols of that
// color.
//
// * Every color in the deck is represented, if it is possible to do so
// in the remaining number of cards.
//
// * Because of the minimum mana symbol count for each represented color,
// splashing cards doesn't add exactly one land of the given type
// (although the land count may still be low for that color).
//
// * The problem of deciding how to round land counts is now easy to
// solve.
let manaSymbolsToAdd = colors.map(color => manaSymbols[color])
let colorsToAdd = []
for (let i = 0; true; i = (i + 1) % colors.length) {
if (manaSymbolsToAdd.every(x => x === 0))
break
if (manaSymbolsToAdd[i] === 0)
continue
colorsToAdd.push(colors[i])
manaSymbolsToAdd[i]--
}

let mainDeckSize = Object.keys(Zones['main'])
.map(x => Zones['main'][x])
.reduce((a, b) => a + b)
let landsToAdd = App.state.deckSize - mainDeckSize

let j = 0
for (let i = 0; i < landsToAdd; i++) {
let color = colorsToAdd[j]
let land = COLORS_TO_LANDS[color]
if (!Zones['main'].hasOwnProperty(land))
Zones['main'][land] = 0
Zones['main'][land]++

j = (j + 1) % colorsToAdd.length
}

App.update()
},
resetLands() {
_resetLands()
App.update()
},
}

function _resetLands() {
Object.keys(COLORS_TO_LANDS).forEach((key) => {
let land = COLORS_TO_LANDS[key]
Zones['main'][land] = Zones['side'][land] = 0
})
}

for (let event in events)
Expand Down Expand Up @@ -218,10 +331,20 @@ function cube() {
}

function clickPack(cardName) {
if (clicked !== cardName)
return clicked = cardName

let index = rawPack.findIndex(x => x.name === cardName)
let card = rawPack[index]

if (clicked !== cardName) {
clicked = cardName
// There may be duplicate cards in a pack, but only one copy of a card is
// shown in the pick view. We must be sure to mark them all since we don't
// know which one is being displayed.
rawPack.forEach(card => card.isAutopick = card.name === cardName)
App.update()
App.send('autopick', index)
return clicked
}

clicked = null
Zones.pack = {}
App.update()
Expand Down
Loading