lib_number_one

A Library Script used for Number One on BvS

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/39671/259484/lib_number_one.js

/** Functions to handle game state **/

/** objects we handle:
    probability object: map from bullet use number to probability we should pick it
    like { "-1":0.25, "0":0.25, "1":0.5 }
    player object: essentially
    struct player {
    char hp;  // ranges from -1 to 5
    unsigned char bullets;  // ranges from 0 to 6
    unsigned char fatigue;  // ranges from 0 to 3
    enum player_type type;  // ranges from 0 to 3
    };
    game state object:
    struct game_state {
    struct player p1;
    struct player p2;
    unsigned char round;  // ranges from 1 to 16
    };
    strategy object:
    member p1Move for p1's first action
    + map from p2's action to p1's doubletime action
**/


// deep copy plain data object
// why is there no builtin equivalent to this
// javascript pls
function copy(obj) {
    return JSON.parse(JSON.stringify(obj));
}

var PREPARATION = 0;
var CATCH = 1;
var PIERCE = 2;
var AURA = 3;

function player_rep(p) {
    var ret = p.hp+2;
    ret = ret | (p.bullets << 3);
    ret = ret | (p.fatigue << 6);
    // prep and aura both act only at the game start so states have same action probabilities
    ret = ret | ((p.type == AURA ? PREPARATION : p.type) << 8);
    return ret;
}

function game_state_rep(x) {
    return ((player_rep(x.p1) << 14) ^
            (player_rep(x.p2) << 4) ^
            (x.round - 1));
}

function act(s, act1, act2) {
    var new_state = copy(s);

    // Resolve round change and HP damage due to round 10+
    if (new_state.round >= 10) {
        new_state.p1.hp--;
        new_state.p2.hp--;
    }
    new_state.round += 1;

  // Resolve bullet count change
  new_state.p1.bullets -= act1;
  new_state.p2.bullets -= act2;
  // should add some asserts here really

  // Resolve fatigue and effects thereof
  if (act1 || (act2 > 0)) {
    // player 1 did not block, or blocked a shot
    new_state.p1.fatigue = 0;
  } else if (act2 <= 0) {
    switch (new_state.p1.fatigue) {
    case 0:
      new_state.p1.fatigue = 1;
      break;
    case 1:
      new_state.p1.fatigue = 2;
      break;
    case 2:
      new_state.p1.fatigue = 3;
      // fall through
    case 3:
      new_state.p1.hp--;
      break;
    }
  }

  if (act2 || (act1 > 0)) {
    // player 2 did not block, or blocked a shot
    new_state.p2.fatigue = 0;
  } else if (act1 <= 0) {
    switch (new_state.p2.fatigue) {
    case 0:
      new_state.p2.fatigue = 1;
      break;
    case 1:
      new_state.p2.fatigue = 2;
      break;
    case 2:
      new_state.p2.fatigue = 3;
      // fall through
    case 3:
      new_state.p2.hp--;
      break;
    }
  }

  // Resolve results of shooting
  if (act1 > 0) {
    if (act2 > 0) {
      // both players shot
      if (act1 > act2) {
        // p2 loses
        new_state.p2.hp = -10;
      } else if (act1 < act2 ||
                 (new_state.p2.type == PIERCE &&
                  new_state.p1.type != PIERCE)) {
        // p1 loses
        new_state.p1.hp = -10;
      } else if (new_state.p1.type == PIERCE &&
                 new_state.p2.type != PIERCE) {
        // p2 loses
        new_state.p2.hp = -10;
      }
    } else if (act2) {
      // p2 reloaded and therefore loses
      new_state.p2.hp = -10;
    } else {
      // p2 blocked
      new_state.p2.hp -= (act1 - 1);
      if (new_state.p2.type == CATCH) {
        // bullet was caught
        new_state.p2.bullets++;
      }
    }
  } else if (act2 > 0) {
    if (act1) {
      // p1 reloaded and loses
      new_state.p1.hp = -10;
    } else {
      // p1 blocked
      new_state.p1.hp -= (act2 - 1);
      if (new_state.p1.type == CATCH) {
        new_state.p1.bullets++;
      }
    }
  }
  // cap bullet count
  if (new_state.p1.bullets > 6) {
    new_state.p1.bullets = 6;
  }
  if (new_state.p2.bullets > 6) {
    new_state.p2.bullets = 6;
  }
  // cap hp count
  if (new_state.p1.hp < 0) {
    new_state.p1.hp = -1;
  }
  if (new_state.p2.hp < 0) {
    new_state.p2.hp = -1;
  }
  return new_state;
}

function pick_action(probabilities, bullets) {
    if (probabilities == undefined || probabilities == null) {
        // trivial/unreachable state, reload
        return -1;
    }

    var accu = Math.random();

    for(var act = -1; act <= bullets; act++) {
        var p = probabilities[act];

        if(p == undefined || p == null) {
            // something weird happened but whatever
            return Math.max(-1, act-1);
        }

        if (p >= accu) {
            return act;
        }

        accu -= p;
    }

    // rounding error might have the sum of probabilities be less than accu
    // in which case
    return bullets;
}

// from text representation of the states, get the state object for
// the state s
// we avoid JSON parsing because we only want 1 state among millions
function get_state(states, s) {
    var rep = game_state_rep(s);
    // essentially
    // "$rep": { .* }
    var reg = new RegExp ('"' + rep + '"[^:]*:[^{]*({[^}]*})', "m");

    //console.log("getting state " + JSON.stringify(s) + " rep " + rep);
    var matches = states.match(reg);
    if (matches == null || matches.length < 2)
        return null;

    var res = matches[1];
    //console.log("results in " + res);
    return JSON.parse(res);
}

// states: map from state ids to probabilities
function get_doubletime_strat(states, s) {
    var strat = {};
    var p1Move = pick_action(get_state(states, s), s.p1.bullets);
    strat["p1Move"] = p1Move;

    for(var p2Move = -1; p2Move <= s.p2.bullets; p2Move++) {
        var s2 = act(s, p1Move, p2Move);
        if (s2.p1.bullets == 0 && s2.p2.bullets == 0)
            s2 = act(s2, -1, -1);

        strat[p2Move] = pick_action(get_state(states,s2), s2.p1.bullets);
    }
    return strat;
}

function get_winchance(wins, state) {
    var rep = game_state_rep(state);
    var reg = new RegExp ("" + rep + ":(.*)");

    var matches = wins.match(reg);
    if (matches == null || matches.length < 2) {
        console.log("N1 win chances: unknown state " + JSON.stringify(state) + " rep " + rep);
        return "-1";
    }
    return matches[1];
}

// parsing used to be based on https://greatest.deepsurf.us/en/scripts/18136-n1bot-inputs

/* A game is displayed in a big <td> whose text goes
   Tier X Event
   Battle Y
   You (win-loss)
   HP: $HP1
   Ammo: $bullets1
   Fatigue: $fatigue1
   Power: Prep|Catch|Pierce|Aura
   <hr>
   P2name
   $P2block (same as P1)
   <hr>
   Round $round
   <hr>
   then some stuff about previous actions,
   then <select> elements to input actions, going
   P1 first move - if P2 reloads - if P2 blocks - if P2 fires 1 to P2 bullets
   The values are according to action_map below.
   We need to parse the 2 player blocks + the round count, send the
   resulting game state to the background script then use the response
   to fill in the selects.
*/
var action_map = {
    "-1": "1^0",
    "0": "2^0",
    "1": "3^1",
    "2": "3^2",
    "3": "3^3",
    "4": "3^4",
    "5": "3^5",
    "6": "3^6"
};

var power_map = {
    "Prep": 0,
    "Catch": 1,
    "Pierce": 2,
    "Aura": 3
}

function parseGame(elmt) {
    var txt = elmt.textContent;

    var state = {p1:{}, p2:{}};

    var hp_patt = /HP: (\d)/g;
    state.p1.hp = parseInt(hp_patt.exec(txt)[1]);
    state.p2.hp = parseInt(hp_patt.exec(txt)[1]);

    var bullet_patt = /Ammo: (\d)/g;
    state.p1.bullets = parseInt(bullet_patt.exec(txt)[1]);
    state.p2.bullets = parseInt(bullet_patt.exec(txt)[1]);

    var fatigue_patt = /Fatigue: (\d)/g;
    state.p1.fatigue = parseInt(fatigue_patt.exec(txt)[1]);
    state.p2.fatigue = parseInt(fatigue_patt.exec(txt)[1]);

    var pow_patt = /Power: (\w+)/g;
    state.p1.type = power_map[pow_patt.exec(txt)[1]];
    state.p2.type = power_map[pow_patt.exec(txt)[1]];

    var round_patt = /Round (\d+)/;
    state.round = parseInt(round_patt.exec(txt)[1]);

    return state;
}