PROGRESS PROBLEM

自分でマンガを描いてみたりとかしたいけど、なかなか筆は進まない…

ArduinoとArduino用液晶ディスプレイを買ったので遊んでみたり【8bit魂】

Amazonのサイバーマンデーやらタイムセールやらで、なんとなくArduino Uno(互換マシン)とArduno用液晶ディスプレイを買ってみたので、なんとなく遊んでみました。
買ったときは、本体¥800、ディスプレイも¥1200とかで、こんな値段で、まがりなりにもコンピューターを呼べるものが買えるのだから、すごい時代になったものです。

で、多分、日本で5人も見てうれしい人がいなさそうな記事です…




↑これを見ると、タッチパネルで、チビキャラを動かして…ゲームとか作れそう…女の子をツンツン突っついて…とかいろいろ妄想しながらマニュアルやらサンプルやらを見たりしたのですが、いろいろなハードルが立ちはだかりました…
結局、めんどくさくなったので、放棄したとこまで、ここに記録しておくことにします。

 最初の問題は、付属ライブラリには、任意のサイズ、色指定でドット絵の類を表示するものがないことです。仕方ないので、ライブラリのソースを基に自作することします。

 次の問題は、メモリの絶望的な少なさです。
ディスプレイの仕様的には、65536色使えるようで、32×32*2(=16bit color)で、2048byte。
Arduino Unoのメモリは…プログラム用エリアを使用しても最大32Kbyte。
ちょっとしたjpeg画像1枚位です。
8bitマシンでも、入門用低価格MSX位のメモリ量です。
65536色指定は現実的ではなさそうです。
そこで、65536色から最大16色使用できることにします。
200×320:65536色中16色
これでも昔の8bitマシンからすると豪華な部類です。(16bitのPC9801でも、640×400 4096色中16色)
これで、1byteで、2ドット分データを持てます。
16*32で、512byte。
とりあえず、色を16色以下に抑えた32×32のドット絵を用意します。

sample-big




チビアリス←原寸サイズ


(そう見えるかはともかく)島田愛里寿を作ってみました。
こんなタイリングやらをしていないデータであれば、圧縮できそうです。
で、単純なランレングス法を採用することにします。
データ仕様は、上位4bitが色番号、下位4bitを連続数-1とします。(最小1回なので、-1すると、最大16まで指定できる)
0x15だと、色番号1を6回連続を意味します。色番号1が赤だとすると、
0x15→■■■■■■

実際に圧縮してみると、
1コマ目、391byte、2コマ目、390byte、各サイズデータが、2byteづつ、
391+390+4=785byte
785/1024=0.77
で、圧縮率77%で、まぁちょっと位は節約できた、感じです。

 そして、最大の問題は描画が、超遅いということです。
32×32でも描画してる様が、ありありと見えます…
この辺も8bit級です…
MSX系やX68000といったハードは、ハードでスプライト表示をサポートしていますが、Arduinoには当然ありません。背景との合成もできません。
移動時の描画処理は、(今となっては悲しい)ベーマガ的テクニックが使用されています。興味があったら見てみてください。現在では得もないとは思いますがwww

 というわけで、これ以上がんばってもいいことなさそうなので、この辺で開発は止めることにしました…
そもそも、Arduinoは機械制御用のものです。
こういう使い方は、あまり向いていません…
ゲーム作りも不可能ではないですし、実際、「arduino game」とかでググると、たくさんヒットするようです。

次は、Raspberry Piでも試してみしょうか…??うーん。
というか、次こそ、なんか絵を描こうか思います…


 で、以下のサンプルは、島田愛里寿が中心から登場し、どっか画面外に向かって歩いて、端に到達するとまた、(なぜか)中心から再登場する、を繰り返します。

※ディスプレイ付属のサンプルを基に作成しています
※使用しているライブラリ(Elegoo_GFXやElegoo_TFTLCD)はAdafruitを基に作成(というか同社製品用にカスタマイズ)されているようです。ですので、
Adafruitのものでも動作するかと思われます。そちらを使用すれば、Elegoo社製品以外のArduino用ディスプレイでも、多少の改造は必要かも知れませんが、以下のサンプルは動作するかと思われます。

https://github.com/adafruit/Adafruit-GFX-Library
https://github.com/adafruit/TFTLCD-Library
#include <avr/pgmspace.h>
#include <Elegoo_GFX.h>    // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library

// The control pins for the LCD can be assigned to any digital or
// analog pins...but we'll use the analog pins as this allows us to
// double up the pins with the touch screen (see the TFT paint example).
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

// When using the BREAKOUT BOARD only, use these 8 data lines to the LCD:
// For the Arduino Uno, Duemilanove, Diecimila, etc.:
//   D0 connects to digital pin 8  (Notice these are
//   D1 connects to digital pin 9   NOT in order!)
//   D2 connects to digital pin 2
//   D3 connects to digital pin 3
//   D4 connects to digital pin 4
//   D5 connects to digital pin 5
//   D6 connects to digital pin 6
//   D7 connects to digital pin 7
// For the Arduino Mega, use digital pins 22 through 29
// (on the 2-row header at the end of the board).

#define CHIP_WIDTH  32
#define CHIP_HEIGHT 32

#define DIRECTION_DIVIDE 64

Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

static const uint8_t  PROGMEM spriteData[] = {
0x0a,0x1a,0x0f,0x02,0x11,0x29,0x30,0x11,0x02,0x41,0x03,0x41,0x04,0x10,0x21,0x49,
0x21,0x30,0x10,0x40,0x52,0x03,0x52,0x40,0x01,0x10,0x30,0x20,0x5b,0x21,0x53,0x20,
0x10,0x04,0x52,0x00,0x10,0x20,0x51,0x29,0x51,0x20,0x51,0x30,0x22,0x05,0x51,0x10,
0x21,0x50,0x22,0x31,0x26,0x50,0x21,0x51,0x22,0x04,0x51,0x00,0x10,0x20,0x50,0x33,
0x10,0x30,0x20,0x10,0x35,0x50,0x20,0x52,0x21,0x10,0x03,0x51,0x00,0x10,0x20,0x50,
0x31,0x20,0x10,0x60,0x10,0x20,0x12,0x20,0x32,0x50,0x20,0x50,0x10,0x51,0x20,0x10,
0x02,0x51,0x01,0x10,0x32,0x20,0x11,0x60,0x10,0x30,0x10,0x61,0x10,0x20,0x32,0x20,
0x11,0x30,0x50,0x20,0x10,0x02,0x50,0x02,0x10,0x31,0x20,0x11,0x62,0x30,0x10,0x62,
0x11,0x21,0x30,0x10,0x00,0x10,0x30,0x20,0x10,0x06,0x10,0x30,0x20,0x10,0x53,0x60,
0x30,0x10,0x60,0x53,0x10,0x20,0x30,0x10,0x00,0x10,0x31,0x10,0x06,0x10,0x30,0x20,
0x10,0x50,0x70,0x80,0x70,0x61,0x10,0x60,0x70,0x80,0x70,0x50,0x10,0x20,0x30,0x10,
0x00,0x10,0x31,0x10,0x06,0x10,0x30,0x20,0x10,0x60,0x70,0x81,0x63,0x81,0x70,0x60,
0x10,0x20,0x30,0x10,0x00,0x10,0x31,0x10,0x06,0x10,0x30,0x20,0x10,0x61,0x81,0x63,
0x81,0x61,0x10,0x20,0x30,0x10,0x01,0x10,0x30,0x10,0x07,0x10,0x20,0x11,0x69,0x11,
0x20,0x10,0x02,0x10,0x30,0x10,0x07,0x10,0x20,0x12,0x67,0x12,0x20,0x10,0x02,0x10,
0x30,0x10,0x07,0x10,0x20,0x10,0x90,0x70,0x50,0x90,0x53,0x90,0x50,0x90,0x11,0x30,
0x10,0x02,0x10,0x30,0x10,0x07,0x10,0x30,0x90,0x71,0x50,0x70,0x53,0x70,0x50,0x70,
0x90,0x10,0x30,0x10,0x03,0x10,0x08,0x10,0x90,0x71,0x90,0x50,0x75,0x50,0x71,0x90,
0x30,0x10,0x03,0x10,0x08,0x90,0x71,0x90,0x00,0x50,0x75,0x50,0x90,0x71,0x10,0x0d,
0x90,0x71,0x90,0x01,0x50,0x90,0x73,0x90,0xa0,0x00,0x90,0x70,0x90,0x0d,0x62,0x02,
0x57,0x00,0x90,0x71,0x90,0x0c,0x61,0x02,0x51,0x40,0x53,0x40,0x51,0x00,0x90,0x61,
0x0f,0x00,0x51,0x47,0x51,0x00,0x61,0x0f,0x00,0x50,0x49,0x50,0x0f,0x02,0x52,0x41,
0x53,0x41,0x52,0x0f,0x01,0x50,0x41,0x51,0x40,0x51,0x40,0x51,0x41,0x50,0x0f,0x02,
0x52,0x41,0x51,0x41,0x52,0x0f,0x05,0x57,0x0f,0x07,0x71,0x03,0x51,0x0f,0x07,0x51,
0x03,0x52,0x0f,0x05,0x52,0x0f,0x01,0x0b,0x1a,0x0f,0x02,0x11,0x29,0x30,0x11,0x02,
0x41,0x03,0x41,0x04,0x10,0x21,0x49,0x21,0x30,0x10,0x40,0x52,0x03,0x52,0x40,0x01,
0x10,0x30,0x20,0x5b,0x21,0x53,0x20,0x10,0x04,0x52,0x00,0x10,0x20,0x51,0x29,0x51,
0x20,0x51,0x30,0x22,0x05,0x51,0x10,0x21,0x50,0x22,0x31,0x26,0x50,0x21,0x51,0x22,
0x04,0x51,0x00,0x10,0x20,0x50,0x33,0x10,0x30,0x20,0x10,0x35,0x50,0x20,0x52,0x21,
0x10,0x03,0x51,0x00,0x10,0x20,0x50,0x31,0x20,0x10,0x60,0x10,0x20,0x12,0x20,0x32,
0x50,0x20,0x50,0x10,0x51,0x20,0x10,0x02,0x51,0x01,0x10,0x32,0x20,0x11,0x60,0x10,
0x30,0x10,0x61,0x10,0x20,0x32,0x20,0x11,0x30,0x50,0x20,0x10,0x02,0x50,0x02,0x10,
0x31,0x20,0x11,0x62,0x30,0x10,0x62,0x11,0x21,0x30,0x10,0x00,0x10,0x30,0x20,0x10,
0x06,0x10,0x30,0x20,0x10,0x53,0x60,0x30,0x10,0x60,0x53,0x10,0x20,0x30,0x10,0x00,
0x10,0x31,0x10,0x06,0x10,0x30,0x20,0x10,0x50,0x70,0x80,0x70,0x61,0x10,0x60,0x70,
0x80,0x70,0x50,0x10,0x20,0x30,0x10,0x00,0x10,0x31,0x10,0x06,0x10,0x30,0x20,0x10,
0x60,0x70,0x81,0x63,0x81,0x70,0x60,0x10,0x20,0x30,0x10,0x00,0x10,0x31,0x10,0x06,
0x10,0x30,0x20,0x10,0x61,0x81,0x63,0x81,0x61,0x10,0x20,0x30,0x10,0x01,0x10,0x30,
0x10,0x07,0x10,0x20,0x11,0x69,0x11,0x20,0x10,0x02,0x10,0x30,0x10,0x07,0x10,0x20,
0x12,0x67,0x12,0x20,0x10,0x02,0x10,0x30,0x10,0x07,0x10,0x20,0x11,0x90,0x50,0x90,
0x53,0x90,0x50,0x70,0x90,0x10,0x30,0x10,0x02,0x10,0x30,0x10,0x07,0x10,0x30,0x10,
0x90,0x70,0x50,0x70,0x53,0x70,0x50,0x71,0x90,0x30,0x10,0x03,0x10,0x08,0x10,0x30,
0x90,0x71,0x50,0x75,0x50,0x90,0x71,0x90,0x10,0x03,0x10,0x09,0x10,0x71,0x90,0x50,
0x75,0x50,0x00,0x90,0x71,0x90,0x0e,0x90,0x70,0x90,0x00,0x50,0x90,0x73,0x90,0xa0,
0x01,0x90,0x71,0x90,0x0c,0x90,0x71,0x90,0x00,0x57,0x02,0x90,0x61,0x0c,0x61,0x90,
0x00,0x51,0x40,0x53,0x40,0x51,0x02,0x61,0x0c,0x61,0x00,0x51,0x47,0x51,0x0f,0x03,
0x50,0x49,0x50,0x0f,0x02,0x52,0x41,0x53,0x41,0x52,0x0f,0x01,0x50,0x41,0x51,0x40,
0x51,0x40,0x51,0x41,0x50,0x0f,0x02,0x52,0x41,0x51,0x41,0x52,0x0f,0x05,0x57,0x0f,
0x07,0x51,0x03,0x71,0x0f,0x06,0x52,0x03,0x51,0x0f,0x0d,0x52,0x0a
};

uint16_t pallet[16] = {
0x7e4d,0x8c49,0xd6d6,0xbdd0,0x73ae,0x20,0xff5a,0xffff,0x6b47,0x855b,0x1922
};

uint16_t frames[2] = {
391,390
};


double x = 0,y = 0,speed = 4;
int step = 0;

int direction = 0;

typedef struct delta {
    double x;
    double y;
};
delta dlt[DIRECTION_DIVIDE];

void setup(void) {
  int i;
  float rad;
 
  Serial.begin(9600);
  Serial.println(F("TFT LCD test"));

#ifdef USE_Elegoo_SHIELD_PINOUT
  Serial.println(F("Using Elegoo 2.4\" TFT Arduino Shield Pinout"));
#else
  Serial.println(F("Using Elegoo 2.4\" TFT Breakout Board Pinout"));
#endif

  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());

  tft.reset();

   uint16_t identifier = tft.readID();
   if(identifier == 0x9325) {
    Serial.println(F("Found ILI9325 LCD driver"));
  } else if(identifier == 0x9328) {
    Serial.println(F("Found ILI9328 LCD driver"));
  } else if(identifier == 0x4535) {
    Serial.println(F("Found LGDP4535 LCD driver"));
  }else if(identifier == 0x7575) {
    Serial.println(F("Found HX8347G LCD driver"));
  } else if(identifier == 0x9341) {
    Serial.println(F("Found ILI9341 LCD driver"));
  } else if(identifier == 0x8357) {
    Serial.println(F("Found HX8357D LCD driver"));
  } else if(identifier==0x0101)
  {    
      identifier=0x9341;
       Serial.println(F("Found 0x9341 LCD driver"));
  }
  else if(identifier==0x1111)
  {    
      identifier=0x9328;
       Serial.println(F("Found 0x9328 LCD driver"));
  }
  else {
    Serial.print(F("Unknown LCD driver chip: "));
    Serial.println(identifier, HEX);
    Serial.println(F("If using the Elegoo 2.8\" TFT Arduino shield, the line:"));
    Serial.println(F("  #define USE_Elegoo_SHIELD_PINOUT"));
    Serial.println(F("should appear in the library header (Elegoo_TFT.h)."));
    Serial.println(F("If using the breakout board, it should NOT be #defined!"));
    Serial.println(F("Also if using the breakout, double-check that all wiring"));
    Serial.println(F("matches the tutorial."));
    identifier=0x9328;
 
  }
  tft.begin(identifier);

  for (i = 0; i < DIRECTION_DIVIDE; i++){
    rad = 2.0 * 3.141592 * (float)i / DIRECTION_DIVIDE;
    dlt[i].x = cos(rad);
    dlt[i].y = -sin(rad);
  }

  tft.fillScreen(pallet[0]);
  x = (tft.width()  - CHIP_WIDTH ) / 2;
  y = (tft.height() - CHIP_HEIGHT) / 2;
  direction = random(0, DIRECTION_DIVIDE);
}



void loop(void) {
  unsigned long start = micros();
  double dx = dlt[direction].x * speed;
  double dy = dlt[direction].y * speed;
  int del_w = 0;
  int del_h = 0;
  int offset = 0;

  if (abs(dx) > 0){
    del_w = ((int)abs(dx)) + 1;   
  }
  if (abs(dy) > 0){
    del_h = ((int)abs(dy)) + 1;   
  }

  if (del_w > 0 && dlt[direction].x > 0.0){
    tft.fillRect(x, y, del_w, CHIP_HEIGHT, pallet[0]);
  }
  if (del_w > 0 && dlt[direction].x < 0.0){
    tft.fillRect(x + CHIP_WIDTH - del_w  , y, del_w, CHIP_HEIGHT, pallet[0]);
  }
  if (del_h > 0 && dlt[direction].y > 0.0){
    tft.fillRect(x, y, CHIP_WIDTH, del_h, pallet[0]);
  }
  if (del_h > 0 && dlt[direction].y < 0.0){
    tft.fillRect(x, y + CHIP_HEIGHT - del_h, CHIP_WIDTH, del_h, pallet[0]);
  }
 
  if ((x + dx) <= 0 || (x + dx) >= tft.width() - (CHIP_WIDTH - 1) || (y + dy) <= 0 || (y + dy) >= tft.height() - (CHIP_HEIGHT - 1)){
    tft.fillRect(x, y , CHIP_WIDTH, CHIP_HEIGHT, pallet[0]);
    x = (tft.width()  - CHIP_WIDTH ) / 2;
    y = (tft.height() - CHIP_HEIGHT) / 2;
    direction = random(0,DIRECTION_DIVIDE);
  } else {
    x += dx;
    y += dy;
  }
 
  offset = step == 1 ? frames[0] : 0;
  drawSprite((int)x, (int)y, spriteData + offset, frames[step], CHIP_WIDTH, CHIP_HEIGHT, pallet);
  step = step == 0 ? 1 : 0;
 
  delay(50);
}

void drawSprite(int16_t x, int16_t y,
            const uint8_t *sprite_data, int16_t s, int16_t w, int16_t h, uint16_t *pallete) {

  int16_t i = 0, j = 0, xx = 0, yy = 0;
  uint8_t c, col, len;

  for(j = 0; j < s; j++){
      c = pgm_read_byte(sprite_data);
      col = (c & 0xF0) >> 4;
      len = c & 0x0F;
      for (i = 0; i < len + 1; i++){
        tft.drawPixel(x+xx, y+yy, *(pallete + col));
        xx++;
        if (xx == w){
          xx = 0;
          yy++;
          if (yy == h){
            return;
          }
        }
      }
      sprite_data++;
  }
}

Javascriptテンプレートエンジン「Handlebars」のifで複雑な条件を書いてみたり

ダー様丸

いつもどおり?個人的なプログラムメモですわ…とはいえ、他にも必要な人はいるかも、という内容ですわ…





 JavascriptのテンプレートエンジンのHandlebarsを使う機会があったのですが、そこでハマったことです。
Hanlebarsだけに関わらず、テンプレートエンジンは、それ自体に複雑な制御構造を持たないものがあります。
これは、そのような制御は、表示するところ(view)で行わず、そこに送るデータを処理するところ(model)でやれよ、というポリシーのためかと思われます。
コレ自体はまっこと正しいのですが、世の中、全て理想通りには行かないもので、必要になるときが結構あります。
そのときは、どんなときよ、となるのですが、サーバから、ある程度まとまった単位でデータを取得して、その値を条件ごとに加工して使いまわしたい、表示しわけたいときです。
まぁ、都度サーバに通信していてはバカバカしい程度の場合ですが。

Handelbarsのifのマニュアルをみても、↓な感じで、(Javascript的な値の)true/falseしか持てない感じになっています。
<div class="entry">
  {{#if author}}
    <h1>{{firstName}} {{lastName}}</h1>
  {{/if}}
</div>
…がいろいろググったり、試した結果、条件(上記ではauthor)は、そこ自体に(Javascript的なtrue/falseを返す)Helperを入れれることがわかりました。
というわけで、自作Helper群を作ってみました…
これらはLisp風の式として実現しています。
Helper中にHeplerが書けるので、よりLisp感溢れる式になります。
Lispを触ったことがない方には、気持ち悪い書式ですが、複雑な式が書けるので、これで良いかと思われます。
また、「{{#if」から「}}」までの間は、改行があっても大丈夫です。

書式:{{#if (演算子Helper 値1 値2) }}
※もちろんunlessでも可
例:test == 1  → {{#if (eq test 1)}}
※その他演算子Helperは下記サンプル参照

    Handlebars.registerHelper('and', function(value1, value2) {
        return value1 && value2;
    });

    Handlebars.registerHelper('or', function(value1, value2) {
        return value1 || value2;
    });

    Handlebars.registerHelper('not', function(value) {
        return !value;
    });

    Handlebars.registerHelper('eq', function(value1, value2) {
        return value1 == value2;
    });

    Handlebars.registerHelper('ne', function(value1, value2) {
        return value1 != value2;
    });

    // <
    Handlebars.registerHelper('lt', function(value1, value2) {
        return value1 < value2;
    });

    // <=
    Handlebars.registerHelper('eqlt', function(value1, value2) {
        return value1 <= value2;
    });

    // >
    Handlebars.registerHelper('gt', function(value1, value2) {
        return value1 > value2;
    });

    // <=
    Handlebars.registerHelper('eqgt', function(value1, value2) {
        return value1 >= value2;
    });
使い方は、まず、ある程度まとまってサーバから取得した前提で、以下のようなデータがあったとします。実際はサーバAjaxとかで取ってくるんじゃないかと思います。
どうでもいいですが、聖グロは、(劇中のイメージより)全体的に背が低いですね…
   var members =[
        {
            'name':'西住 みほ',
            'school':'大洗女子学園',
            'position':'車長',
            'height':158
        },
        {
            'name':'武部 沙織',
            'school':'大洗女子学園',
            'position':'通信手',
            'height':157
        },
        {
            'name':'五十鈴 華',
            'school':'大洗女子学園',
            'position':'砲手',
            'height':163
        },
        {
            'name':'秋山 優花里',
            'school':'大洗女子学園',
            'position':'装填手',
            'height':157
        },
        {
            'name':'冷泉 麻子',
            'school':'大洗女子学園',
            'position':'操縦手',
            'height':145
        },
        {
            'name':'西住 まほ',
            'school':'黒森峰女学園',
            'position':'車長',
            'height':163
        },
        {
            'name':'ダージリン',
            'school':'聖グロリアーナ女学園',
            'position':'車長',
            'height':158
        },
        {
            'name':'アッサム',
            'school':'聖グロリアーナ女学園',
            'position':'砲手',
            'height':150
        },
        {
            'name':'オレンジペコ',
            'school':'聖グロリアーナ女学園',
            'position':'装填手',
            'height':144
        }
    ];
で、使い方は、こんな感じ。
Handlebarsと、それ以外にjQueryを読み込んでいる前提です。

こんなテンプレートがあった場合、
        <script id="sample-template" type="text/x-handlebars-template">
            <div class="part">
                <h3>全員</h3>
                <ul>
                    {{#each members}}
                        <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                    {{/each}}
                </ul>
            </div>
            <div class="part">
                <h3>車長のみ(==)</h3>
                <ul>
                    {{#each members}}
                        {{#if (eq position '車長')}}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>
            <div class="part">
                <h3>車長以外(!=)</h3>
                <ul>
                  {{#each members}}
                      {{#if (ne position '車長')}}
                          <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                      {{/if}}
                  {{/each}}
                </ul>
            </div>
            <div class="part">
                <h3>160cm以下の車長(<= && ==)</h3>
                <ul>
                    {{#each members}}
                        {{#if (and (eqlt height 160) (eq position '車長'))}}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>
            <div class="part">
                <h3>砲手か装填手(== || ==)</h3>
                <ul>
                    {{#each members}}
                        {{#if (or (eq position '砲手') (eq position '装填手'))}}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>
            <div class="part">
                <h3>砲手か装填手以外(!(== || ==))</h3>
                <ul>
                    {{#each members}}
                        {{#if (not (or (eq position '砲手') (eq position '装填手')))}}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>

            <div class="part">
                <h3>150cmより小さい装填手か、160cmより大きい車長か、150cmより大きい砲手((< && ==) || (> && ==) || (> && ==))</h3>
                <ul>
                    {{#each members}}
                        {{#if (or (or (and (lt height 150) (eq position '装填手'))(and (gt height 160) (eq position '車長')))(and (gt height 150) (eq position '砲手')))}}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>

            <div class="part">
                <h3>150cmより小さい装填手か、160cmより大きい車長か、150cmより大きい砲手、で無い人(!((< && ==) || (> && ==) || (> && ==)))</h3>
                <ul>
                    {{#each members}}
                        {{#if
                            (not
                                (or
                                    (or
                                        (and (lt height 150) (eq position '装填手'))
                                        (and (gt height 160) (eq position '車長'))
                                    )
                                    (and (gt height 150) (eq position '砲手'))
                                )
                            )
                        }}
                            <li>{{name}}:{{school}}:{{position}}:{{height}}cm</li>
                        {{/if}}
                    {{/each}}
                </ul>
            </div>
        </script>




こんな感じで呼び出す。

    $(function(){
        var source   = $("#sample-template").html();
        var template = Handlebars.compile(source);

         var html    = template({'members':members});
        $('#target').html(html);
    });
こんな↓結果が表示されるはず・・・

全員

  • 西住 みほ:大洗女子学園:車長:158cm
  • 武部 沙織:大洗女子学園:通信手:157cm
  • 五十鈴 華:大洗女子学園:砲手:163cm
  • 秋山 優花里:大洗女子学園:装填手:157cm
  • 冷泉 麻子:大洗女子学園:操縦手:145cm
  • 西住 まほ:黒森峰女学園:車長:163cm
  • ダージリン:聖グロリアーナ女学園:車長:158cm
  • アッサム:聖グロリアーナ女学園:砲手:150cm
  • オレンジペコ:聖グロリアーナ女学園:装填手:144cm

車長のみ(==)

  • 西住 みほ:大洗女子学園:車長:158cm
  • 西住 まほ:黒森峰女学園:車長:163cm
  • ダージリン:聖グロリアーナ女学園:車長:158cm

車長以外(!=)

  • 武部 沙織:大洗女子学園:通信手:157cm
  • 五十鈴 華:大洗女子学園:砲手:163cm
  • 秋山 優花里:大洗女子学園:装填手:157cm
  • 冷泉 麻子:大洗女子学園:操縦手:145cm
  • アッサム:聖グロリアーナ女学園:砲手:150cm
  • オレンジペコ:聖グロリアーナ女学園:装填手:144cm

160cm以下の車長(<= && ==)

  • 西住 みほ:大洗女子学園:車長:158cm
  • ダージリン:聖グロリアーナ女学園:車長:158cm

砲手か装填手(== || ==)

  • 五十鈴 華:大洗女子学園:砲手:163cm
  • 秋山 優花里:大洗女子学園:装填手:157cm
  • アッサム:聖グロリアーナ女学園:砲手:150cm
  • オレンジペコ:聖グロリアーナ女学園:装填手:144cm

砲手か装填手以外(!(== || ==))

  • 西住 みほ:大洗女子学園:車長:158cm
  • 武部 沙織:大洗女子学園:通信手:157cm
  • 冷泉 麻子:大洗女子学園:操縦手:145cm
  • 西住 まほ:黒森峰女学園:車長:163cm
  • ダージリン:聖グロリアーナ女学園:車長:158cm

150cmより小さい装填手か、160cmより大きい車長か、150cmより大きい砲手((< && ==) || (> && ==) || (> && ==))

  • 五十鈴 華:大洗女子学園:砲手:163cm
  • 西住 まほ:黒森峰女学園:車長:163cm
  • オレンジペコ:聖グロリアーナ女学園:装填手:144cm

150cmより小さい装填手か、160cmより大きい車長か、150cmより大きい砲手、で無い人(!((< && ==) || (> && ==) || (> && ==)))

  • 西住 みほ:大洗女子学園:車長:158cm
  • 武部 沙織:大洗女子学園:通信手:157cm
  • 秋山 優花里:大洗女子学園:装填手:157cm
  • 冷泉 麻子:大洗女子学園:操縦手:145cm
  • ダージリン:聖グロリアーナ女学園:車長:158cm
  • アッサム:聖グロリアーナ女学園:砲手:150cm


ダー様丸
以上ですわ…

JavaでSchemeを簡易実装(の移植)してみたり

存在自体を忘れたblogを久々に更新してみたりします。
久しぶりの更新はプログラム記事です…

Schemeは、ゲーム本体よりサントラの方が売れたことで有名な、
レジェンド 80'S「ザ・スキーム サウンドトラック」
ゲーム・ミュージック
サイトロン・デジタルコンテンツ (2002-08-21)
売り上げランキング: 246,917
…ではなく、Lispの方言の一種のプログラム言語です。

ことの発端はAmazonプライム会員が1ヶ月1冊、Kindleで、ただで読める本が一部にあるんですが、そこから何かないか探したころ、
やさしいLispの作り方: C言語で作るミニミニLisp処理系
笹川 賢一 (2016-01-24)
売り上げランキング: 18,244
がみつかり、なんとなく気になって読んでみようと思ったことからでした。
Lispは全く知らなかったのですが、一度はやってみたかった言語でした。
で、読んだ感じですが、数学ガールとかの系統のラノベ系の内容で、わりと分かりやすく、プライム会員であれば一読する価値は充分あります。
読んでみた感じ、作れそうな気がしたので、最初はこちらをJavaに(で?)移植しようとしたんですが、C言語とJavaが、パラダイム的に?遠いせいか移植が難航しました。
なんか他にないかとググってみたところ、
((Pythonで) 書く (Lisp) インタプリタ)
と続きの
((Pythonで) 書く ((さらに良い) Lisp) インタプリタ)
が発見され、そちらを元にしてみることにしました。(上記記事は翻訳で元記事へのリンクあり)
というわけで、この世には完璧にSchemeの実装がいくらでもある(上記サイトの翻訳元にもJavaでの実装があるみたい)なか、純粋に私個人の学習用に実装してみました。
所謂車輪の再発明であり、以前の車輪に比べ勝っている要素は無いです。
  • Lispは知らない(Lispの勉強の題材として実装)
  • Pythonは知らない(せっかくだから、こっちも勉強する)
の二重苦状態で、知らない言語のソースをもとに、知らない言語の勉強用に、知らない言語自体を実装しようというのは、わりと頭がおかしい気がしますが、こういうのもたまにはいい気がします。

そもそも正しい動作自体が分からないので、動作は下記参考サイトを参考にしています。
お気楽 Scheme プログラミング入門
もうひとつの Scheme 入門
Scheme入門 スーパービギナー編
情報システムA 2012年度後期
また、一部動作はSchemeの実装の一種のGaucheを参考にして作成しています。
Gaucheマニュアル

Scheme(Lisp)を知らない人間が何故か実装したので、Scheme(Lisp)として正しい動作をしているかよくわからないのですが、公開すると誰かが動作確認してくれるかも知れないので、一応公開することにしてみます。

違ってる個所があった場合、報告していただけるとありがたいです。

このプログラムは、Schemeの一部のみを実装しています。なので出来てしかるべきことの多くは出来ないかと思います。出来ること、できないことはJUnitのテストコードに書いています。(この範囲で動作検証がとれています)

■ダウンロード
jarとソースのダウンロードと実行方法は、
https://github.com/progressproblem/TinyScheme
に置いてあります。興味がある方はお試しください。
また、使える関数の追加や、ファイル出力への対応は容易に行えるかと思います。
それらのプラグイン化も面白いかと思います。
Java8にて作成されています。一部Java8の機能のラムダ式を使っていますが、簡単に使わなく書き直せるはずです。
IntelliJ Idea13にて作成していますがプロジェクトファイル類はコミットしていません。
また、AntやMavenなどのプロジェクトファイルは存在しません。
コンパイル済みのjarとテストに必要なJUnitのjar(実行だけの場合は不要)もリポジトリにコミットしています。

■最後に
元ソースの
((Pythonで) 書く ((さらに良い) Lisp) インタプリタ)
は、いろいろ参考になるテクニックも多いので記事を読むだけで、参考になります。(ローカル変数の実装方法とか、パターンマッチングでの字句解析とかいろいろ)
JavaとPythonとでも言語的パラダイム(というのか?)が違うので、こっちも割りと苦労しました。
Pythonで1行(場合によっては暗黙値で0行)で書けるものが10行、20行とかかります。

今回Javaで作ってみた感じですと、Schemeベースの独自言語とか作れそうで夢は広がります。

あと、私も古代祐三氏のThe Schemeのサントラだけは持っています(ゲームは持ってない)

プロフィール

かぢ

QRコード
QRコード
  • ライブドアブログ