Newer
Older
accessory_repo / AbilityTimer / xTable.pde
// This software is distributed under the terms of the MIT License.
// Copyright (c) 2018, 2019 molelord
// All rights reserved.

class OcrEngine {
  short [][] table;
  int        w;
  int        h;
  float      binThreshold;
  int        errorThreshold;
  OcrEngine(short[][] _table, int _w, int _h,
    float _binThreshold, int _errorThreshold) {
    table = _table;
    w     = _w;
    h     = _h;
    binThreshold   = _binThreshold;
    errorThreshold = _errorThreshold;
  }
  // -1:failed 0to9:number 10:black square 11:white square
  int ocr(PImage img) {
    img.filter(THRESHOLD, binThreshold);

    short[] line = new short[h];
    for (int py = 0; py < h; py++) {
      line[py] = 0;
      for (int px = 0; px < w; px++) {
        int c = img.pixels[w*py + px];
        c = getG(c);
        line[py] <<= 1;
        line[py] |= (c > 0) ? 1 : 0;
      }
      dbg.pln("y" + nf(py,2) + ": " +
        hex(line[py], 3) + " : " + binary(line[py], w));
    }

    dbg.p("OCR diff ");

    int   minDiff    = w*h;
    int   minDiffNum = 0;
    int[] totalDiff  = new int[table.length+2];  // 2 is black & white square
    for (int num = 0; num < totalDiff.length; num++) {

      // Perform a normal comparison and a comparison shifted by 1 dot,
      // and the better one is adopted.
      int[] diff = new int[2];
      for (int sft = 0; sft < diff.length; sft++) {
        for (int i = 0; i < h; i++) {
          int tmp = 0;
          if (num < table.length) {
            tmp = (table[num][i]<<sft) ^ line[i];
          } else if (num == table.length) {
            tmp = 0 ^ line[i]; // compare with Black square
          } else {
            tmp = ((1<<w)-1) ^ line[i]; // compare with White square
          }

          int bits = Glbl.countHotbits(tmp);
          if (bits >= 3) {
            // If the difference is large, the evaluation is
            // greatly reduced.
            bits *= 2;
          }
          diff[sft] += bits;
        }
      }
      totalDiff[num] = (diff[0] < diff[1]) ? diff[0] : diff[1];

      if (totalDiff[num] < minDiff) {
        minDiff    = totalDiff[num];
        minDiffNum = num;
      }
      dbg.p(nf(num) + ":" + nf(totalDiff[num]) + " ");
    }
    dbg.pln("");

    // Allow even a slight difference.
    if (minDiff < errorThreshold) {
      dbg.pln("OCR success " + nf(minDiffNum) +
          " diff:" + nf(totalDiff[minDiffNum]));
      return minDiffNum;
    }
    dbg.pln("OCR fail:");
    return -1;
  }
}

class OcrEngine2 extends OcrEngine {
  final int width  = 17;
  final int height = 17;
  final int WALL_L =  1;
  final int WALL_R =  2;
  final int WALL_U =  4;
  final int WALL_D =  8;
  final int DOT    = 16;
  final int ENCLOSED = WALL_L | WALL_R | WALL_U | WALL_D;
  final int OPEN_L   =          WALL_R | WALL_U | WALL_D;
  final int OPEN_R   = WALL_L |          WALL_U | WALL_D;
  final int OPEN_LD  =          WALL_R | WALL_U;
  final int OPEN_RD  = WALL_L |          WALL_U;
  int [] line;
  int charfeed;
  OcrEngine2(int _w, int _h, float _binThreshold, int _charfeed) {
    super(null, _w, _h, _binThreshold, 25);
    line = new int[height];
    charfeed = _charfeed;
  }

  // -1:failed 0to9:number 10:black square 11:white square
  int ocr(PImage img) {
    img.resize(width, height);
    img.filter(THRESHOLD, binThreshold);

    for (int py = 0; py < height; py++) {
      line[py] = 0;
      for (int px = 0; px < width; px++) {
        int c = img.pixels[width*py + px];
        c = getG(c);
        line[py] <<= 1;
        line[py] |= (c > 0) ? 1 : 0;
      }
      dbg.pln("y" + nf(py,2) + ": " +
        hex(line[py], 4) + " : " + binary(line[py], width));
    }

    int result = -1;
    if (
      searchWalls(8,4)  == ENCLOSED &&
      searchWalls(8,8)  == DOT &&
      searchWalls(8,12) == ENCLOSED) {
      result = 8;
    }
    else if (
      searchWalls(8,4)  == ENCLOSED &&
      searchWalls(8,8)  == ENCLOSED &&
      searchWalls(8,12) == ENCLOSED) {
      result = 0;
    }
    else if (
      searchWalls(8,4)  == OPEN_R &&
      searchWalls(8,12) == ENCLOSED) {
      result = 6;
    }
    else if (
      searchWalls(8,4)  == ENCLOSED &&
      searchWalls(8,12) == OPEN_L) {
      result = 9;
    }
    else if (
      searchWalls(8,8)  == ENCLOSED &&
      searchWalls(8,13) == OPEN_LD) {
      result = 4;
    }
    else if (
      searchWalls(8,4)  == OPEN_L &&
      searchWalls(8,12) == OPEN_L) {
      result = 3;
    }
    else if (
      searchWalls(8,4)   == OPEN_L &&
      searchWalls(7,7)   == OPEN_L &&
      searchWalls(12,13) == OPEN_R) {
      result = 2;
    }
    else if (
      searchWalls(8,4)  == OPEN_R &&
      searchWalls(8,7)  == DOT &&
      searchWalls(8,11) == OPEN_L) {
      result = 5;
    }
    else if (
      searchWalls(8,4)   == OPEN_L &&
      searchWalls(10,8)  == DOT &&
      searchWalls(12,13) == OPEN_RD) {
      result = 7;
    }
    else if (
      searchWalls(8,3)  == DOT &&
      searchWalls(8,8)  == DOT &&
      searchWalls(8,12) == DOT &&
      searchWalls(8,14) == DOT &&
      searchWalls(1,8)  == WALL_R &&
      (searchWalls(13,8) & WALL_L) == WALL_L) {
      result = 1;
    }
    else {
      int black = 0;
      int white = 0;
      for (int py = 0; py < height; py++) {
        if (line[py] == 0) {
          black++;
        }
        else if (line[py] == (1<<width)-1) {
          white++;
        }
      }
      if (black >= width) {
        result = 10;
      }
      else if (white >= width) {
        result = 11;
      }
    }

    dbg.pln("OcrEngine2.ocr() result:" + result);
    return result;
  }

  int searchWalls(int posX, int posY) {
    int px = 0;
    int py = 0;
    int hot = 1<<(width-1);

    if ((line[posY] & (hot>>>posX)) > 0) {
      // It is inside the charactor's line.
      return DOT;
    }

    int walls = 0;
    // search horizontally
    for (px = 0; px < width; px++) {
      if (px == posX) continue;

      if ((line[posY] & (hot>>>px)) > 0) {
        if (px < posX) {
          walls |= WALL_L;
        }
        else {
          walls |= WALL_R;
        }
      }
    }
    // search Vertically
    for (py = 0; py < height; py++) {
      if (py == posY) continue;

      if ((line[py] & (hot>>>posX)) > 0) {
        if (py < posY) {
          walls |= WALL_U;
        }
        else {
          walls |= WALL_D;
        }
      }
    }

    return walls;
  }
}

OcrEngine2 OcrEnhLv;
OcrEngine2 OcrEnhNext;
OcrEngine2 OcrInfoLv;
OcrEngine2 OcrInfoNext;
OcrEngine2 OcrPlayers;
OcrEngine  OcrV;
OcrEngine2 OcrSkillLv;
OcrEngine2 OcrSkillNext;
OcrEngine2 OcrDmmEnhLv;
OcrEngine2 OcrDmmEnhNext;
OcrEngine2 OcrDmmInfoNext;
OcrEngine2 OcrWeeklyAcceP;

void xTableInitialize() {
  // v, 8x8 dots @ Battle Window
  final short[][] CharV = {
    /*
    {0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3E,0x00}, // L
    {0x00,0x90,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xBE,0x80}, // L(selected)
    */
    {0x00,0x22,0x22,0x36,0x14,0x1C,0x1C,0x00}, // v
    {0x00,0x00,0x22,0x22,0x36,0x14,0x1C,0x1C}, // v @ DMM
  };

  //                                     w  h   bin   err/charfeed
  OcrEnhLv       = new OcrEngine2(        9, 11, 0.8f, 9+1);
  OcrEnhNext     = new OcrEngine2(        9, 11, 0.8f, 9+1);
  OcrInfoLv      = new OcrEngine2(       10, 15, 0.8f, 10+2);
  OcrInfoNext    = new OcrEngine2(        8, 11, 0.8f, 8+2);
  OcrPlayers     = new OcrEngine2(        9, 12, 0.8f, 9+1);
  OcrV           = new OcrEngine (CharV,  8,  8, 0.8f, 21);
  OcrSkillLv     = new OcrEngine2(       10, 14, 0.8f, 10+1);
  OcrSkillNext   = new OcrEngine2(        9, 12, 0.7f, 9+1);
  OcrDmmEnhLv    = new OcrEngine2(        9, 12, 0.7f, 9+1);
  OcrDmmEnhNext  = new OcrEngine2(        9, 12, 0.7f, 9+1);
  OcrDmmInfoNext = new OcrEngine2(        9, 12, 0.7f, 9+1);
  OcrWeeklyAcceP = new OcrEngine2(       13, 17, 0.8f, 0);
}

// Thanks to https://神姫プロジェクト.攻略wiki.com/index.php?アクセ強化
static final short[] AccessoryNextExpTable = {
  //       0   1   2   3   4   5   6   7   8   9
  /* 0*/   0, 10, 15, 20, 25, 30, 35, 40, 45, 50,
  /*10*/  55, 60, 65, 70, 75, 80, 85, 90, 95,100,
  /*20*/ 120,140,160,180,200,220,240,260,280,300,
  /*30*/ 320,340,360,380,400,420,440,460,480,500,
  /*40*/ 520,540,560,580,600,620,640,660,680,700,
};

// Thanks to https://神姫プロジェクト.攻略wiki.com/index.php?アクセ強化
static final int[] AccessoryBaitExpTable = {
  //        0   1   2   3   4   5   6   7   8    9
  /* 0*/    0,  0,  1,  2,  3,  5,  7,  9, 12,  15,
  /*10*/   18, 22, 26, 30, 35, 40, 45, 57, 69,  81,
  /*20*/   95,109,123,139,155,171,189,207,225, 245,
  /*30*/  265,285,318,351,384,420,456,492,531, 570,
  /*40*/  609,651,693,735,780,825,870,934,998,1062,
  /*50*/ 1130,
};

// Thanks to https://神姫プロジェクト.攻略wiki.com/index.php?ウェポンスキル一覧
static final int[] TempoWeaponNextExpTable = {
  //       0   1   2   3   4   5   6    7    8    9
  /* 0*/   0, 30, 60, 90,120,160,200, 250, 300, 360,
  /*10*/ 420,490,560,640,720,810,900,1000,1100,1210,
};

static final float[] DmmGemTimeTable = {
  12.0, 20.0, -1,   // Mon
  12.5, 21.0, -1,   // Tue
  -1,   18.0, 21.0, // Wed
  -1,   19.0, 22.0, // Tur
  -1,   20.0, 23.0, // Fri
  12.0, 18.0, 22.0, // Sat
  12.5, 19.0, 23.0, // Sun
};

static final float[] NutakuGemTimeTable = {
  12.0, 19.0, -1,   // Mon
  12.5, 19.5, -1,   // Tue
  -1,   18.0, 22.5, // Wed
  -1,   19.0, 23.0, // Tur
  -1,   19.5, 23.5, // Fri
  12.0, 18.0, 22.0, // Sat
  12.5, 19.0, 23.0, // Sun
};