RaspberryPi + Python3.9でmatplotlib/pandas

nnt339es.hatenablog.com

折角ビルドしたのにmatplotlibもpandasもpipできなかったので結局Docker。

curl -fsSL https://get.docker.com/ | bash

参考

docs.docker.jp (accessed on 2021/06/10)

Dockerfile

適当にDockerfile書いて終わり。

FROM python:3.9.5-buster as builder

WORKDIR /opt/app
COPY requirements.lock /opt/app
RUN pip install --upgrade pip \
 && pip install -r requirements.lock

FROM python:3.9.5-slim-buster as runner

WORKDIR /opt/app 


COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages


CMD ["/bin/bash"]

nnt339es.hatenablog.com

RaspberryPiへのPythonインストールメモ

RaspberryPiにUbuntu入れて適当にPythonインストールしてたらSSL関連でpipが詰まったのでそのメモ。

$ sudo apt update
# ビルド用ライブラリのインストール (これを忘れていた。)
$ sudo apt install build-essential libbz2-dev libdb-dev \
  libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
  libncursesw5-dev libsqlite3-dev libssl-dev \
  zlib1g-dev uuid-dev tk-dev

# ソースのダウンロード
$ wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tar.xz

# 解凍
$ tar -zxvf Python-3.9.5.tar.xz

$ cd Python-3.9.5
# ビルドコンフィグ
$ ./configure
# コンパイル
$ make
# インストール
$ sudo make install

# Version確認
$ python3.9 --version
# ワーキングディレクトリの作成
$ mkdir work
$ cd work

# venv環境作成
$ python3.9 -m venv venv

# venv環境起動
$ source venv/bin/activate

参考

www.python.jp

OpenGLで遊ぶ

OpenGLを触るきっかけがあったのでfreeglutで遊んでみた。

Homer Simpson

かわいい。

よく使ったオブジェクト

void gluSphere(
    GLUquadric *qobj, 
    GLdouble radius, 
    GLint slices, 
    GLint stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。 初学者なのでquadricオブジェクトが何か知らない。 gluNewQuadric()で全て誤魔化した。

GLdouble radius

半径。

GLint slices

経度の分割数。 数が大きいほど滑らか。

GLint stacks

緯度の分割数。

円筒

void gluCylinder(
    GLUquadric *qobj,
    GLdouble   baseRadius,
    GLdouble   topRadius,
    GLdouble   height,
    GLint      slices,
    GLint      stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。

GLdouble baseRadiius

底面の半径。

GLdouble topRadius

上面の半径。

GLdouble height

円筒の高さ

GLint slices

経度の分割数。

GLint stacks

緯度の分割数。

座標の平行移動

void glTranslated(
    GLdouble x, 
    GLdouble y, 
    GLdouble z
);
GLdouble x, y, z

平行移動するベクトル。

座標の回転

void glRotated(
    GLdouble angle,
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble angle

回転する角度。

GLdouble x, y, z

回転軸ベクトル。

拡大・縮小

void glScaled(
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble x, y, z

それぞれの方向に対する拡大率。

座標系の一時的な変更

void glPushMatrix(void);

座標系の保存。

void glPopMatrix(void);

座標系の元に戻す。

e.g.

glPushMatrix();
    /* 座標系を変更する処理 */
    glTranslated(1.0, 0.0, 0.0); // ... (#)
    glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);
glPopMatrix();

/* ここでは(#)の処理が反映されない */
glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);

C言語だから仕方ないけど、Pythonなら絶対withステートメント使う。

色付け

void glMaterialfv(
    GLenum face, 
    GLenum pname, 
    const GLfloat *params
);
GLenum face

GL_FRONT: 表 GL_BACK: 裏 GL_FRONT_AND_BACK: 表と裏

GLenum pname

GL_DIFFUSE: 拡散反射 GL_SPECULAR: 鏡面反射 GL_AMBIENT: 環境光

const GLfloat *params

色を指定する。 (0.0, 1.0)のRGBと透過率(α値)で指定する。

// e.g.
GLfloat red[] = {1.0, 0.0, 0.0, 1.0};

まだこんな少数の関数しか覚えてないけど、誤魔化しながらホーマー描けたので満足。

参考文献

docs.microsoft.com

(accessed on 2021/05/23)

#include <GL/glut.h>
#include <math.h>
#define PI2 (3.1415*2)

GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
GLfloat jeans[] = {0, 0.5, 0.7, 1.0};
GLfloat yellow[] = {1.0, 0.9, 0.0, 1.0};
GLfloat stubble[] = {0.596, 0.486, 0.165, 1.0};
GLfloat grey[] = {0.4, 0.4, 0.4, 1.0};
GLfloat black[] = {0, 0, 0, 1.0};

void Homer();
void head();
void body();
void arms();
void legs();

void eyes();
void mouse();
void nose();
void ears();
void hand(int isRight);


void Homer(void) {
    /* head */
    glPushMatrix();
        glTranslatef(0, 0.2, 2.2);
        glScalef(0.8, 0.8, 0.8);
        head();
    glPopMatrix();

    /* body */
    body();

    /* legs */
    glPushMatrix();
        glTranslated(0.0, 0.0, -2.5);
        legs();
    glPopMatrix();
}

void head(void) {
    GLdouble headHeight = 1.05;
    GLdouble headRadius = 0.63;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glRotatef(20, 1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), headRadius+0.02, headRadius, headHeight, 30, 10);
            glPushMatrix();
                glTranslated(0, 0.0, 0.01);
                glScalef(1, 1, 0.9);
                gluSphere(gluNewQuadric(), headRadius+0.02, 30, 30);
            glPopMatrix();
            glPushMatrix();
                glTranslated(0, 0, headHeight);
                glScalef(1, 1, 0.8);
                gluSphere(gluNewQuadric(), headRadius, 30, 30);
            glPopMatrix();
        glPopMatrix();

        /* hear side */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
        int numSpike = 30;
        double step = PI2 / numSpike;
        GLdouble sideHearHeight = 0.45;
        GLdouble sideHearRadius = headRadius + 0.01;
        GLdouble sideHearWidth = 2.0;
        int start = 5, end = 23;
        glLineWidth(sideHearWidth);
        glBegin(GL_LINE_STRIP);
        for (int i = 0; i < numSpike; i++) {
            if (i < start) continue;
            if (i > end) continue;
            glVertex3d(sideHearRadius * sin(i * step), sideHearRadius * cos(i * step), sideHearHeight);
            glVertex3d(sideHearRadius * sin((i+0.5) * step), sideHearRadius * cos((i+0.5) * step), sideHearHeight+0.3);
        }
        glVertex3d(sideHearRadius * sin((end+1) * step), sideHearRadius * cos((end+1) * step), sideHearHeight);
        glEnd();
        
        /* hear top */
        GLdouble topHearHeight = headHeight + 0.3;
        GLdouble between = 0.1;
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
                glTranslated(0, (i == 0)? between : -between, topHearHeight);
                hear();
            glPopMatrix();
        }
    glPopMatrix();

    /* eyes */
    GLdouble eyePos[] = {0, 0.4, 0.60};
    glPushMatrix();
        glTranslated(eyePos[0], eyePos[1], eyePos[2]);
        eyes();
    glPopMatrix();

    /* mouse */
    GLdouble mousePos[] = {0, 0.29, -0.08};
    glPushMatrix();
        glTranslated(mousePos[0], mousePos[1], mousePos[2]);
        mouse();
    glPopMatrix();

    /* nose */
    GLdouble nosePos[] = {0, 0.5, 0.36};
    glPushMatrix();
        glTranslated(nosePos[0], nosePos[1], nosePos[2]);
        nose();
    glPopMatrix();

    /* ears */
    GLdouble earPos[] = {0, 0, 0.27};
    glPushMatrix();
        glTranslated(earPos[0], earPos[1], earPos[2]);
        ears();
    glPopMatrix();
}


void body(void) {
    /* arms */
    GLdouble armPos[] = {0, 0, 1.55};
    glPushMatrix();
        glTranslated(armPos[0], armPos[1], armPos[2]);
        arms();
    glPopMatrix();

    glPushMatrix();
        /* waist */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
        gluSphere(gluNewQuadric(), 1.0, 30, 30);
        
        /* chest */
        glTranslatef(0, 0, -0.18);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        gluCylinder(gluNewQuadric(), 1.15, 0.4, 1.9, 30, 10);

        /* neck */
        glTranslated(0, 0, 1.7);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, 0.6, 30, 10);
    glPopMatrix();
}

void arms(void) {
    GLdouble sleeveLength = 0.5;
    GLdouble sleeveRadius = 0.4;
    GLdouble armLength = 1.0;
    GLdouble pos[3] = {0.4, 0, 0};
    GLfloat angle = -30;

    /* right arm */
    glPushMatrix();
        glTranslated(pos[0], pos[1], pos[2]);
        glRotatef(angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 1);
    glPopMatrix();

    /* left arm */
    glPushMatrix();
        glTranslated(-pos[0], pos[1], pos[2]);
        glRotatef(-angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 0);
    glPopMatrix();
}

void sleeve_and_arm(GLdouble sleeveRadius, GLdouble sleeveLength, GLdouble armLength, int isRight) {
    glPushMatrix();
        /* sleeve */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        glTranslated(0, 0, -sleeveRadius);
        gluSphere(gluNewQuadric(), sleeveRadius, 30, 30);
        glTranslated(0, 0, -sleeveLength);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, sleeveLength, 30, 30);

        /* arm */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        glTranslated(0, 0, -armLength);
        gluCylinder(gluNewQuadric(), 0.3, 0.35, armLength+sleeveLength, 30, 30);

        /* hand */
        hand(isRight);
    glPopMatrix();
}

void hand(int isRight) {
    GLdouble thumbLength = 0.25;
    GLdouble indexLength = 0.36;
    GLdouble middleLength = 0.36;
    GLdouble pinkieLength = 0.35;
    double t = (isRight)? 1.0 : -1.0;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.3, 30, 30);
    glPopMatrix();
    
    /* thumb */
    glPushMatrix();
        glRotatef(20*t, 0, 1, 0);
        glTranslated(-0.11*t, 0.16, -thumbLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.11, thumbLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* index */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, 0.12, -indexLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, indexLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* middle */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.17*t, -0.03, -middleLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, middleLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* pinlie */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, -0.14, -pinkieLength);
        gluCylinder(gluNewQuadric(), 0.09, 0.11, pinkieLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.09, 30, 30);   
    glPopMatrix();
}

void legs(void) {
    GLdouble legLength = 2.3;
    GLfloat pos[3] = {0.37, 0, 0};
    GLdouble legRadius = 0.5;
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
            glTranslatef((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            gluCylinder(gluNewQuadric(), legRadius, legRadius, legLength, 30, 10);
            
            /* shoe */
            glMaterialfv(GL_FRONT, GL_DIFFUSE, grey);
            glScalef(1, 1.4, 0.45);
            glTranslated(0, 0.1, -0.2);
            gluSphere(gluNewQuadric(), legRadius, 30, 30);
        glPopMatrix();
    }
}

void eyes(void) {
    GLdouble pos[3] = {0.24, 0, 0};
    GLdouble eyeSize = 0.24;
    
    glPushMatrix();
        glScalef(1, 0.8, 1);
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, white); // color
            gluSphere(gluNewQuadric(), eyeSize, 30, 30);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, black); // color
            glTranslated(0, eyeSize-0.01, 0);
            glScalef(1, 0.6, 1);
            gluSphere(gluNewQuadric(), 0.06, 30, 30);
            glPopMatrix();
        }
    glPopMatrix();
}

void mouse(void) {
    glMaterialfv(GL_FRONT, GL_DIFFUSE, stubble); // color
    glPushMatrix();
        glScalef(1, 1, 0.68);
        glTranslated(0, -0.12, 0.10);
        gluSphere(gluNewQuadric(), 0.6, 20, 20);
    glPopMatrix();

    glPushMatrix();
        glTranslated(0, -0.06, -0.04);
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.5, 20, 20);
    glPopMatrix();
}

void nose(void) {
    GLdouble noseSize = 0.12;
    GLdouble noseHeight = 0.15;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    glPushMatrix();
        glRotatef(90, -1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), noseSize, noseSize, noseHeight, 20, 20);
            glTranslated(0, 0, noseHeight);
            gluSphere(gluNewQuadric(), noseSize, 20, 20);
        glPopMatrix();
    glPopMatrix();
}

void ears(void) {
    GLdouble pos[3] = {0.61, -0.2, 0};
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glRotatef(100, 0, (i==0)? 1 : -1, 0);
            glRotatef(30, -1, 0, 0);
            glutSolidTorus(0.089, 0.08, 30, 30);
        glPopMatrix();
    }
}

void hear() {
    int n = 30;
    double step = PI2 / n;
    GLdouble hearRadius = 0.4;
    GLdouble hearWidth = 2.0;
    glLineWidth(hearWidth);
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < n; i++) {
        glVertex3d(hearRadius * cos(i * step), 0, hearRadius * sin(i * step));
    }
    glEnd();
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Homer();
    glutSwapBuffers();
}


void init(void) {
    
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    GLfloat lightPos0[] = {-5, 15, 10, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);
    
    glEnable(GL_LIGHT1);
    GLfloat lightPos1[] = {5, 15, 10, 1};
    glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, 1, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(8, 15, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(600, 600);
    glutCreateWindow("Homer Simpson");
    glutDisplayFunc(display);
    init();
    glutMainLoop();
    return 0;
}

pyYGOBot(ygo-agent)の今後

一人回しの時間がもったいないから機械学習によって一人回しと同等の知見が得られないかと画策しているpyYGOBotであるが、今後の作業について整理したいと思う。

パッケージ化

いわゆる”AI”と呼ばれる機械学習の部分をパッケージとして独立させることで、さまざまなアプリケーションに導入できるようにしたいと思う。

パッケージ化に伴い、名前をtf_agentに倣ってygo-agentに変更する。

パソコンに不慣れな人々もユーザー層として捉えるにはGUI・実行ファイル形式が不可欠だ。その際パッケージとして”AI”を組み込んだ方が互いに独立して開発・テストできるため生産性が上がるだろうという判断だ。

ドキュメントの整備

パッケージとして誰かに使ってもらうにはドキュメントが不可欠だ。

個人プロジェクトである以上モチベーションによって開発にブランクができてしまうため、あれば復帰する時に便利なはず。

元々可読性の高さを意識してコーディングしているため数か月経ってもコードは読めたが、それでも脳内にプログラムを構築するのに数時間はかかったためできれば節約したい。

テストコードの実装

設計図のないままリファクタリングするのに疲れた。というかテストするの自体疲れた。自動化して楽になりたい。

ドキュメントを整備する過程で設計が浮かびあがてくるはずだから、ドキュメントを書きながらテストコードを書きたい。

サーバー(ホスト)の実装

実質ADSを自作するということ。
もちろんGUIはなし。
サードパーティから独立できれば、頒布・導入・拡張がやりやすくなるはず。

Goで実装

いずれはGoで実装したいなあとかなんとか。

計算機の購入

現状のショボいビジネスノートじゃ学習がままならないので、並列計算機を実費で購入するか、あるいはクラウドファンディング等で資金を募るか。

そのためにも十分にテストされた機能するコードを完成させる必要があるなと。

おわりに

計画してから半年経つけど、まだ「とりあえず動くコード」しかできてなくて、AIの学習効率向上、精度改善みたいな段階にいけてないのが辛い。

「金銭でも名誉でもなんでも良いけど何か利益でんの?」とか考えだすと途端にモチベが落ちる。暇だからやっている。プログラマになるつもりはない。でも将来的(数年後)には職が欲しい。あほくさ。

春休みにはJulia習得して、暇つぶしに競技プログラミングやるときにはJulia使ってる。近い未来に数値シミュレーションするときはFortranではなく、きっとJulia使う。

JavaScriptきらいTypeScriptすき。
流行りのRustとRやって言語オタク極めるか?



何かに束縛されている。何だろう。おそらく将来に束縛されている。無垢なあの頃のように将来から解放されたい。とりあえず進んでおけば手が広いという数年後の自画像があるあの頃に。

これ以上選択を引き延ばせば専門オタクに何も勝てない虚無の存在になってしまうという恐怖と専門性に拘束されてしまう嫌悪が現代の若者の生きづらさではないか?

Numpy Style の docstring

リファクタリングしたり、他の人に移譲する時にドキュメントがあったほうが良いよなと思い、Numpy Styleのdocstringを調べた。


numpydoc.readthedocs.io

記述例

def add(a: float, b: float=0.0) -> float:
    """
    Add two floats.

    Parameters
    ----------
    a : float
        Description of parameter `a`.
    b : float, default 0.0
        Description of parameter `b`.

    Returns
    -------
    float
        The sum of two floats.

    Yields
    ------
    Type
        Description of generated value.

    Receives
    --------
    Type
        Explaination of parameters passed to a generator's .send() method.

    Raises
    ------
    Exception
        Description of errors.

    See Also
    --------
        Related functions witch users may not be aware of, etc...

    Notes
    -----
        Additional information.

    Examples
    --------
    >>> add(3.0, 4.0)
    7.0

    >>> add(1.0)
    1.0
    """

    return a + b



class MyClass:
    """
    MyClass

    Attributes
    ----------
    id : int
        Identfier.
    value : float
        Value.
    name : str
        Name.

    Methods
    -------
    get_id()
        Return its id.
    set_value(value)
        Set value
    """
    def __init__(self, id: int, value: float, name: str) -> None:
        self.id: int = id
        self.value: float = value
        self.name: str = name

    def get_id(self) -> int:
        """
        Return its id.

        Returns
        -------
        int
            id
        
        Examples
        --------
        >>> A = MyClass(2, 3.0, 'name')
        >>> A.get_id()
        2
        """
        return self.id

    
    def set_value(self, value: float) -> None:
        """
        Set value.

        Parameters
        ----------
        value : float
            an float value to set.
        
        Examples
        --------
        >>> A = MyClass(0, 0.0, 'name')
        >>> A.value
        0.0
        >>> A.set_value(3.0)
        >>> A.value
        3.0
        """
        self.value = value

    
if __name__ == '__main__':
    import doctest
    doctest.testmod()

Sections

アンダーラインハイフンでセクションを区切る。
Short Summary
一行の簡単な説明。
Parameters
パラメータ名 : 型(, default デフォルト値)
パラメータの説明。
Returns

戻り値の説明。
Raises
Raiseしうる例外の説明。
See Also
ユーザーが知覚して無さそうな関連した関数とかの情報。
Notes
補足説明。
Examples
実行例。

所感

VSCodeとかだとホバーした時にドキュメントが表示されて便利。
確かdocstringからドキュメントを生成するツールもあったはず。

ただ明らかに1ファイルの文字数は増えていて、逆に読みにくくならないか心配。

Dockerfile

将来的にOSが変わったりしてライブラリで詰まったりしたら嫌だなぁ、と思い開発環境をDockerで整えることにした。

Docker

コンテナ作るやつ。カーネルはホストOSのものを使うからゲストOSが要らなくて計量らしい。

ライブラリ隔離だけならvenvだけでも良いんだけど、MacとかLinuxに変わった時に入らないライブラリがあると面倒だから、初めからコンテナで開発環境を隔離することにした。

Dockerfile

コンテナの原型となる「イメージ」の設計図。
(https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html, accessed on 2021/04/15)

FROM python:3.9.4-buster

WORKDIR /opt/app

COPY requirements.txt /opt/app
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
FROM イメージのベースを指定
LABEL イメージに付けるラベルの指定
RUN イメージの最上位のレイヤーで実行されるコマンドを設定
CMD コンテナ実行時のデフォルト処理を設定
EXPOSE コンテナがリッスンするポートを設定
ENV 環境変数の設定
COPY ローカルファイルをコンテナにコピー
ENTRYPOINT コンテナ起動時の処理
VOLUME イメージ内にデータを永続的に保持する際に使用する データベース、ストレージ、設定用ストレージetc...
USER 非ルートユーザへ変更
WORKDIR ワークディレクトリを設定 コマンドを実行する場所

VSCodeとの連携

Remote - Container

コンテナ内でVSCodeを使っているかのようにできる。
marketplace.visualstudio.com


実際にアプリケーションとしてデプロイする時はマルチステージビルドしてイメージを計量にしたり、実行ユーザーを変更したり、環境変数を整えたりしてから使う。

ハタ人間パーティー

100階攻略したことを踏まえて、構成してみたかったパーティーについて。

パーティ構成

逆手パ

第一スキル 第二スキル 第三スキル 第四スキル 隊列
主人公 指揮 回収 暗視 警戒 後衛
夏奈 根性 回避 逆手 前衛
白瀬 暗視 回避 曲撃ち 逆手 前衛
るりか 冷静 治療 応援 逆手 後衛

ハンドガン使い3人の逆手パーティー
るりかの応援によって火力を底上げする。
スキルの自由枠が一つあるので例えば《回収》が付けば素材回収パーティーになる。

速射パ

第一スキル 第二スキル 第三スキル 第四スキル 隊列
主人公 指揮 回収 暗視 警戒 後衛
リコ 頑丈 反撃 前衛
委員長 根性 解析 射撃 速射 後衛
エリ 警戒 逃げ足 治療 速射 後衛

リコを盾に委員長とエリで後ろから狙撃するパーティー
速射パで避けられない相手からの一撃をリコの「頑丈」「反撃」で捌いていく。
エリを逆手役にしたらイマイチだったため狙撃手として起用。
火力が欲しければリコも狙い撃ちすればよい。

逆手パ

第一スキル 第二スキル 第三スキル 第四スキル 隊列
主人公 指揮 回収 暗視 警戒 後衛
平山 逆手 応援 体捌き 後衛
大神 根性 底力 体捌き 逆手 前衛
ユイ 回避 急所狙い 逆手 前衛

刀剣急所狙い逆手パーティー
「急所狙い」+「逆手」がどれくらい強力かはよく知らない。
平山の無駄使い?

パーティに必要なスキル

2章を攻略する上で重要となる点がいくつかある。

第一に戦力だ。生半可な火力で挑めばため技を食らって全滅である。また、レベルによって押し切るという戦法も90階から敵のレベルが100を超えることから難しいと思われる。そこで必要なのが火力を保証するスキルと武器を大量生産しやすくする《回収》である。これらのスキルのサポートによって、2章攻略の難易度を下げるのが重要だ。

第二に安定性だ。深層に向かうにつれ、戦闘を回避できるならば回避したい敵が出てくる。そんなときに逃走不可能では全滅は避けられない。したがって、《暗視》スキルは必須とする。また、全滅の原因として多いのは先制からの恐怖やため技だ。これらのリスクを軽減するために《警戒》組み込む必要がある。

第三に周回性だ。深層を目指すには素材集めやレベル上げで数多くダンジョンを潜る必要がある。周回のストレス軽減が重要で、そこで必要となるのが《暗視》《回収》ということになる。

以上より、パーティに必須となるスキルは

  • 火力スキル
  • 《回収》
  • 《暗視》
  • 《警戒》

ということになる。

火力パーティ

火力スキルとして代表的なのが《速射》と《逆手》である。

一部の人は全て《速射》で十分と考えているが、それでは味気ないし、速射パーティー自体にも欠点がある。

一つ目の欠点は運用開始時期の遅さである。1ターン狙い撃ちとして運用するには《速射》レベル5以上、素早さ14~程度のステータスが必要だ。主人公/平山は別として、普通にダンジョンを潜りながらこれを達成するのは早くて70階、遅ければ80階に到達する頃だろう。《速射》が重要となるのは80階以降とは言え、それまでの階層は《速射》無しで攻略する必要があるため、レベル上げや素材集めがやや面倒くさい。

もう一つの欠点は戦闘時間の長さである。1ターン狙い撃ちしても相手の攻撃を受けることは避けられない。また、仕留め損ねるともっと時間がかかり周回する上で面倒くさいことになる。特に深層でもよく遭遇する雑魚集団(ゴキブリやUFO)には手間取り、オートにしても時間がかかるのだ。

速射パは深層攻略には有効だが、ダンジョン周回という点では不利なことを押さえておきたい。そして、レベル上げや素材集めにはダンジョン周回が不可欠なのだ。


一方で、逆手パはスキルレベル1でも武器さえあれば機能するため40階程度から導入することができる。素早さと手数を重視するため雑魚戦でも強く、敵の行動を待たずして戦闘を終えることができる。これは周回する上で非常に有用だ。

逆に、逆手パの欠点は素早さと攻撃力(力or器用さ)を重視するがゆえに、防御力と恐怖耐性を放棄するので先制や恐怖で全滅する危険性が潜んでいることだ。


したがって、速射パと逆手パを混合したパーティー構築が望ましいと考えられる。

主人公のスキル

主人公のスキルは3つとも自由、かつ全てのパーティに主人公が含まれることから、その3つを選別することが重要であることは言うまでもない。

どのパーティーでも使用するという点から、『パーティに必要なスキル』で考察したスキルの中から選ぶのが最適だろう。

主人公に《速射》は必要か?

主人公に火力スキルを付けるとしたら《速射》だろう。
《逆手》も魅力的だが、速射パで主人公の《逆手》が死ぬことを考えるとやはり《速射》だと思う。
主人公は《DQT2000》を常備すると思うのでその点も噛み合っている。

しかし、主人公は素早さが命という点から見れば《速射》するよりハンドガンや刀剣の方が向いていると思われる。

主人公をパーティ内に最速して、速射×2 + 主人公2発 + 壁1発の攻撃で戦闘終了するならば主人公に《速射》は必要ない。

逆手パという観点から見ても、逆手6発 + 主人公1発 o r逆手12発 + 主人公1発で戦闘は終了するためわざわざ狙い撃ちする必要がない。

主人公を火力として捉える必要があるのは単騎や2人でダンジョンを攻略するような特殊なプレイだけで、それを目的としない今回の理論では主人公の《速射》は必要ないと考えている。

主人公に素早さは必要か?

主人公に素早さが必要なのは溜め技に対してパーティ全員が防御できるようにするためである。
防御すれば全滅は避けられるかもしれない(仲間が死なないとは言ってない)が、溜め技の前に倒すために速射パや逆手パを組むのだから、結局主人公の素早さは最速である必要はない。

どうしても避けられない溜め技として先制からの溜めが挙げられるが、この場合主人公がパーティー内最遅でも一応全員防御はできる。
実際には《煙幕》を使っての逃走が最善手なため2章では《煙幕》を2つ常備しよう。

主人公の恐怖耐性

主人公の火力に拘らないで《DQT2000》に《バカ》を付けて常備させれば十分だと思う。

雑談

意外にアクセス数があって、未だに需要があるのかと驚くパワポケ11裏サクセス。

購入して10年くらいでようやく「あれ」を撃破したが、落とした衝撃でカセットが抜けて全滅したり、全滅した一軍を救助しにいった二軍が全滅したり、子供の頃の私にはひどいゲームだった。
しかも全滅したのは手間暇かけてスキル粘りした一軍で、何度1章からやりなおしたことか。

このゲームの攻略法を上げるとすれば、

  • 一軍ではなく二軍でダンジョン攻略
  • 60~80階が正念場で手間暇をかけてその階層で装備を整える

といったところか。

念のため三軍も育成していたけれど、途中で面倒になって3軍はLv70くらいで止まっている。
まあ大体60階くらいで欲張って深く潜った時に全滅するので、100階は遠いけど50~60階でしっかり装備を整えてから60~65階に臨むべき。
「あれ」は委員長の《キュウビ》狙い撃ちや逆手《ドラゴイーター》計4発で7~8割削れるので大したことはなかった。

また興味が湧いたら一章からやり直そうかな。