For a little toy project of mine I need to have data available in a certain format in order to be able to feed into a Recurrent Neural Network (Long short term memory), it uses HearthStone card data that is available in JSON format.
Example input:
{
"id":"EX1_306",
"name":"Succubus",
"type":"Minion",
"faction":"Neutral",
"rarity":"Free",
"cost":2,
"attack":4,
"health":3,
"text":"<b>Battlecry:</b> Discard a random card.",
"flavor":"Warlocks have it pretty good.",
"artist":"Matt Dixon",
"collectible":true,
"race":"Demon",
"playerClass":"Warlock",
"howToGet":"Unlocked at Level 1.",
"howToGetGold":"Unlocked at Level 40.",
"mechanics":[
"Battlecry"
]
}
Example output:
Basic | Spell | Common | Paladin | Blessing of Kings | 4 | Give a minion +4/+4. <i>(+4 Attack/+4 Health)</i>
Classic | Minion | Legendary | Neutral | Dragon | Onyxia | 9 | 8 | 8 | <b>Battlecry:</b> Summon 1/1 Whelps until your side of the battlefield is full.
Basic | Minion | Free | Neutral | Beast | River Crocolisk | 2 | 2 | 3 |
Basic | Weapon | Free | Warrior | Fiery War Axe | 2 | 3 | 2 |
Classic | Minion | Legendary | Neutral | Pirate | Captain Greenskin | 5 | 5 | 4 | <b>Battlecry:</b> Give your weapon +1/+1.
Classic | Minion | Legendary | Neutral | None | Tinkmaster Overspark | 3 | 3 | 3 | <b>Battlecry:</b> Transform another random minion into a 5/5 Devilsaur or a 1/1 Squirrel.
Goblins vs Gnomes | Minion | Common | Neutral | Mech | Flying Machine | 3 | 1 | 4 | <b>Windfury</b>
Please note that the example input describes only a single card, while the example output describes an unrelated set of cards.
I decided to implement this in Groovy, and am looking forward to receiving lots of feedback as this is the first standalone program I've written in Groovy.
import groovy.json.JsonSlurper
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
/**
* @author Frank van Heeswijk
*/
class Main {
static String cardDataUrl = "http://hearthstonejson.com/json/AllSets.json"
static Path targetPath = Paths.get(System.getProperty("user.home"), "HearthStoneTrainData", "data.txt")
static long targetFileSize = 1024L * 1024L
static void main(String[] args) {
saveToTargetFile(convertToOneLiners(splitIntoSets(retrieveCardData())))
}
static Object retrieveCardData() {
JsonSlurper jsonSlurper = new JsonSlurper()
jsonSlurper.parse(new URL(cardDataUrl), StandardCharsets.UTF_8.name())
}
static Map<String, List> splitIntoSets(Object cardsJson) {
def map = new HashMap()
cardsJson.each { set, cards ->
map[set] = cards
}
map
}
static List<String> convertToOneLiners(Map<String, List> cardsBySet) {
def list = new ArrayList()
cardsBySet.each { set, cards ->
cards.each { card ->
if (card.collectible) {
list.add(convertToOneLiner(set, card))
}
}
}
list
}
static String convertToOneLiner(String set, Object cardJson) {
def type = cardJson.type
def rarity = cardJson.rarity ?: "Token"
def race = cardJson.race ?: "None"
def playerClass = cardJson.playerClass ?: "Neutral"
def name = cardJson.name
def cost = cardJson.cost
def attack = cardJson.attack
def health = cardJson.health
def durability = cardJson.durability
def text = cardJson.text ?: ""
switch (type) {
case "Minion":
return "${set} | ${type} | ${rarity} | ${playerClass} | ${race} | ${name} | ${cost} | ${attack} | ${health} | ${text}"
case "Spell":
return "${set} | ${type} | ${rarity} | ${playerClass} | ${name} | ${cost} | ${text}"
case "Enchantment":
return "${set} | ${type} | ${playerClass} | ${text}"
case "Weapon":
return "${set} | ${type} | ${rarity} | ${playerClass} | ${name} | ${cost} | ${attack} | ${durability} | ${text}"
case "Hero":
return "${set} | ${type} | ${rarity} | ${playerClass} | ${name} | ${health}"
case "Hero Power":
return "${set} | ${type} | ${rarity} | ${playerClass} | ${name} | ${cost} | ${text}"
default:
throw new IllegalArgumentException("Unknown type: ${type} in ${cardJson}")
}
}
static void saveToTargetFile(List<String> cardOneLiners) {
def copyCardOneLiners = new ArrayList(cardOneLiners)
Files.deleteIfExists(targetPath)
Files.createFile(targetPath)
def writer = Files.newBufferedWriter(targetPath, StandardCharsets.UTF_8, StandardOpenOption.APPEND)
writer.withCloseable {
while (targetPath.size() < targetFileSize) {
println("Progress: ${targetPath.size()}/${targetFileSize}")
Collections.shuffle(copyCardOneLiners)
copyCardOneLiners.each { line ->
writer.append(line)
writer.newLine()
}
}
}
}
}