Skip to content

Latest commit

 

History

History
312 lines (264 loc) · 8.79 KB

README.md

File metadata and controls

312 lines (264 loc) · 8.79 KB

Gohbem

Gohbem is an optimized judgemental library that computes PvP rankings for Pokemon GO.

This is a rewrite of node version https://github.com/Mygod/ohbem

Features

  • Little cup/great league/ultra league rankings
  • Multiple level caps (level 50/51)
  • Customizable CP/level caps
  • Evolutions support
  • Mega evolutions support (including unreleased Mega)
  • Tyrogue evolutions support
  • Gender-locked evolutions support
  • Unevolvable costumes support
  • Tied PvP ranks (for example, 13/15/14 and 13/15/15 Talonflame are both UL rank 1 at L51, followed by 14/14/14 being UL rank 3)
  • Functionally perfect support
  • Optional built-in caching
  • Faster than node :)

Current State

  • CalculateTopRanks is broken.
  • Everything else is fine.

Usage

package main

import (
    "github.com/UnownHash/gohbem"
)

func main() {
    var leagues = map[string]gohbem.League{                          // Leagues configuration & caps.
        "little": {                                                   // Cap for master is ignored.
            Cap:            500,
            LittleCupRules: true,
        },
        "great": {
            Cap:            1500,
            LittleCupRules: false,
        },
        "ultra": {
            Cap:            2500,
            LittleCupRules: false,
        },
        "master": {
            Cap:            0,
            LittleCupRules: false,
        },
    }
    levelCaps := []int{50, 51}                                        // Level caps.

    ohbem := gohbem.Ohbem{Leagues: leagues, LevelCaps: levelCaps}

    err = ohbem.FetchPokemonData()                                    // Fetch latest stable MasterFile...
    err = ohbem.WatchPokemonData()                                    // ...automatically watch remote for changes...
    err = ohbem.LoadPokemonData("masterfile.json")                    // ...or load from file

    // ...
}

Examples

Provided examples are marshaled. Each method is returning defined structs. Read Documentation for details.

QueryPvPRank

entries, err := ohbem.QueryPvPRank(605, 0, 0, 1, 1, 4, 12, 7)
{
  "great":[
    {"pokemon":605,"cap":50,"value":1444316,"level":50,"cp":1348,"percentage":0.84457,"rank":3158},
    {"pokemon":605,"cap":51,"value":1472627,"level":51,"cp":1364,"percentage":0.85568,"rank":3128},
    {"pokemon":606,"cap":40,"value":1639371,"level":21,"cp":1493,"percentage":0.97919,"rank":197,"capped":true}
  ],
  "little":[
    {"pokemon":605,"cap":40,"value":320801,"level":14.5,"cp":494,"percentage":0.95123,"rank":548,"capped":true},
    {"pokemon":606,"cap":40,"value":302917,"level":7,"cp":486,"percentage":0.93383,"rank":1056,"capped":true}
  ],
  "ultra":[
    {"pokemon":606,"cap":40,"value":3519629,"level":40,"cp":2489,"percentage":0.97294,"rank":651},
    {"pokemon":606,"cap":50,"value":3519629,"level":40,"cp":2489,"percentage":0.97294,"rank":745,"capped":true}
  ]
}

CalculateTopRanks (broken)

entries, err := ohbem.CalculateTopRanks(5, 605, 0, 0, 0)
{
  "great":[
    {"value":1710113,"level":50,"cp":1498,"percentage":1,"rank":1,"attack":8,"defense":15,"stamina":15,"cap":50},
    {"value":1699358,"level":48.5,"cp":1500,"percentage":0.99371,"rank":2,"attack":11,"defense":15,"stamina":15,"cap":50},
    {"value":1699151,"level":50,"cp":1489,"percentage":0.99359,"rank":3,"attack":7,"defense":15,"stamina":15,"cap":50},
    {"value":1698809,"level":49,"cp":1500,"percentage":0.99339,"rank":4,"attack":10,"defense":15,"stamina":15,"cap":50},
    {"value":1698192,"level":49.5,"cp":1494,"percentage":0.99303,"rank":5,"attack":9,"defense":15,"stamina":14,"cap":50},
    {"value":1698192,"level":49.5,"cp":1499,"percentage":0.99303,"rank":5,"attack":9,"defense":15,"stamina":15,"cap":50},
    {"value":1720993,"level":51,"cp":1497,"percentage":1,"rank":1,"attack":6,"defense":15,"stamina":15,"cap":51},
    {"value":1717106,"level":51,"cp":1500,"percentage":0.99774,"rank":2,"attack":7,"defense":14,"stamina":15,"cap":51},
    {"value":1710113,"level":50,"cp":1498,"percentage":0.99368,"rank":3,"attack":8,"defense":15,"stamina":15,"cap":51},
    {"value":1709818,"level":51,"cp":1487,"percentage":0.99351,"rank":4,"attack":5,"defense":15,"stamina":15,"cap":51},
    {"value":1709291,"level":50.5,"cp":1498,"percentage":0.9932,"rank":5,"attack":7,"defense":15,"stamina":15,"cap":51}
  ],
  "little":[
    {"value":337248,"level":14,"cp":500,"percentage":1,"rank":1,"attack":0,"defense":14,"stamina":15,"cap":40},
    {"value":335954,"level":14,"cp":500,"percentage":0.99616,"rank":2,"attack":0,"defense":15,"stamina":13,"cap":40},
    {"value":334290,"level":14,"cp":498,"percentage":0.99123,"rank":3,"attack":0,"defense":13,"stamina":15,"cap":40},
    {"value":333943,"level":14,"cp":500,"percentage":0.9902,"rank":4,"attack":1,"defense":15,"stamina":11,"cap":40},
    {"value":333571,"level":14,"cp":499,"percentage":0.98909,"rank":5,"attack":1,"defense":12,"stamina":15,"cap":40}
  ]
}

FilterLevelCaps

entries, err := ohbem.QueryPvPRank(661, 0, 0, 1, 15, 15, 14, 1)
filter := ohbem.FilterLevelCaps(entries["great"], []int{51})
[
  {"pokemon":662,"cap":51,"value":1743985,"level":41.5,"cp":1493,"percentage":0.94736,"rank":1328},
  {"pokemon":663,"cap":40,"value":1756548,"level":23.5,"cp":1476,"percentage":0.94144,"rank":2867,"capped":true}
]

Benchmark

TL;DR

  • Go QueryPvPRank is 5 times faster than node with disabled cache.
  • Go QueryPvPRank is 10 times faster than node with enabled cache.

Specs & versions

# Gohbem 0.7.3
# Ohbem 1.4.1
# cpu: 12th Gen Intel(R) Core(TM) i9-12900KF

$ go version
go version go1.19.4 linux/amd64
$ node --version
v16.14.0

QueryPvPRank

Gohbem

$ time ./main  # cache disabled ; maxPokemonId = 2
QueryPvPRank iterated 13068 in 1m23.355235694s

real    1m23.406s
user    1m29.580s
sys     0m4.767s

$ time ./main  # cache enabled ; maxPokemonId = 200
QueryPvPRank iterated 1306800 in 3.821691967s

real    0m3.898s
user    0m4.094s
sys     0m0.315s

Ohbem (node)

$ time node main.js  # cache disabled ; maxPokemonId = 2
queryPvPRank iterated 13068 in 418771ms

real    6m58.854s
user    7m7.976s
sys     0m17.332s

$ time node main.js  # cache enabled ; maxPokemonId = 200
queryPvPRank iterated 1306800 in 38922ms

real    0m39.038s
user    0m46.019s
sys     0m3.972s

Test scripts

main.js

const Ohbem = require('ohbem');
const pokemonData = require('./master-test.json');

async function test() {
    const ohbem = new Ohbem({
        leagues: {
        little: {
            little: false,
            cap: 500,
        },
        great: {
            little: false,
            cap: 1500,
        },
        ultra: {
            little: false,
            cap: 2500,
        },
        master: null,
    },
        levelCaps: [40, 50, 51],
        pokemonData,
        cachingStrategy: Ohbem.cachingStrategies.balanced // change
    });

    const maxPokemonId = 200; // change
    const maxAttack = 10;
    const maxDefense = 5;
    const maxStamina = 10;
    const maxLevel = 5;
    let counter = 0;

    const start = Date.now();
    for (let p = 1; p <= maxPokemonId; p++) {
        for (let a = 0; a <= maxAttack; a++) {
            for (let d = 0; d <= maxDefense; d++) {
                for (let s = 0; s <= maxStamina; s++) {
                    for (let l = 1; l <= maxLevel; l += 0.5) {
                        ohbem.queryPvPRank(p, 0, 0, 0, a, d, s, l);
                        counter++;
                    }
                }
            }
        }
    }
    const elapsed = Date.now() - start;
    console.log(`queryPvPRank iterated ${counter} in ${elapsed}ms`);
}

test();

main.go

package main

import (
	"fmt"
	"github.com/UnownHash/gohbem"
	"time"
)

func mainOne() {
	var leagues = map[string]gohbem.League{
		"little": {
			Cap:            500,
			LittleCupRules: false,
		},
		"great": {
			Cap:            1500,
			LittleCupRules: false,
		},
		"ultra": {
			Cap:            2500,
			LittleCupRules: false,
		},
		"master": {
			Cap:            0,
			LittleCupRules: false,
		},
	}

	levelCaps := []int{40, 50, 51}

	ohbem := gohbem.Ohbem{Leagues: leagues, LevelCaps: levelCaps, DisableCache: false}  // change
	_ = ohbem.LoadPokemonData("master-test.json")

	const (
		maxPokemonId = 200 // change
		maxAttack    = 10
		maxDefense   = 5
		maxStamina   = 10
		maxLevel     = 5
	)
	var counter uint

	start := time.Now()
	for p := 1; p <= maxPokemonId; p++ {
		for a := 0; a <= maxAttack; a++ {
			for d := 0; d <= maxDefense; d++ {
				for s := 0; s <= maxStamina; s++ {
					for l := 1.0; l <= maxLevel; l = l + 0.5 {
						ohbem.QueryPvPRank(p, 0, 0, 0, a, d, s, l)
						counter++
					}
				}
			}
		}
	}
	elapsed := time.Since(start)
	fmt.Printf("QueryPvPRank iterated %d in %s\n", counter, elapsed)

func main() {
	mainOne() // bench go
}