OmniAICreator/Qiita-1.07M huggingface dataset:
[SpringBoot] The request was rejected because the URL contained a potentially malicious String "//"

#### 問題
SpringBoogのアプリケーションをデプロイしたところ、タイトルのエラーが発生。
この動作自体は、Spring Security が不正なURLでのリクエストを拒否するもので正しい。
ただ、アプリケーション側では、このような不正なURL（連続スラッシュ）は見当たらない。

#### 原因
nginx のリバースプロキシの設定に問題があった。

```
  location /xxx {
    proxy_pass http://zzz.com:8080/xxx/; ※最後にスラッシュが入っている
    proxy_redirect default;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
```

このスラッシュがあるがために、アプリケーション内の別ページに移動しようとした際に、(ttp//zzz.com:8080/xxx//yyy) のように "//" が含まれるURLでアクセスされてしまっていた。

#### 対処

```
  location /xxx {
    proxy_pass http://zzz.com:8080/xxx; ※最後のスラッシュを削除
    proxy_redirect default;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
```
エレキ素人が何か考える（その13）：トライステートバッファ

# トライステートバッファ
何かしらのタイミングで理由もなく、秋月電子で購入した部品のひとつに、トライステートバッファがあった。電子回路におけるトライ(3)ステートバッファとは、"0"、"1"以外の状態として"Z"（ハイインピーダンス）を持つもの。[この説明](https://toshiba.semicon-storage.com/jp/semiconductor/knowledge/e-learning/micro-intro/chapter1/logic-circuit-3-state-buffer-1.html)が端的である。

なぜ購入したのかわからず（名前に惹かれたか？）、しばらく放置していた。が、最近、これを用いて何ができるのか、素人なりに調べると、1つの信号線に複数の論理回路が物理（電気）的に接続されている状態で、どれか1つの回路を接続することに使われることを知った（他はハイインピーダンスにして接続しない）。コンピュター内の利用例として、次のサイトの「スリー・ステート・バス・バッファ」のところの説明がナイスと感じている。
- [【5分で覚えるIT基礎の基礎】デジタル回路の基礎の基礎　第3回](https://xtech.nikkei.com/it/members/ITPro/ITBASIC/20021011/1/)

## 何をするか
信号線の選択をする例として、[デジタル回路のWiki](https://ja.wikipedia.org/wiki/デジタル回路)の「スリーステートバッファ」のデータセレクターの記載がわかりやすく、かつ、簡単であったので、これを少々modifyすることにした。

### 回路イメージ
後述する、実際にトライした回路とは多少異なるが、イメージ的には下記のようなもの（回路シミュレーター「[falstad](https://www.falstad.com/circuit/circuitjs.html)」利用）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/f0ad1298-45e3-3779-d0e7-92231de34448.gif", width=70%>
中央2つがトライステートバッファ、片方に"1"を、もう片方には"0"を入力し、片方は導通がある状態を、もう片方にはそれがない状態を作り出す。LEDの点灯により、その状態を視覚化する。回路シミュレーターでは、矩形波及びNOT回路により、"1"および"0"を交互に作り出している（後述する実回路では、矩形波の代わりにPCからの入力を用いた）。

## 用いたトライステートバッファ
秋月電子で購入した「４回路３ステートバッファ　ＳＮ７４ＨＣ１２５Ｎ」。ただし、本記事執筆時点では販売されていないようだ。データシートは、[ここ](https://www.ti.com/lit/ds/symlink/sn74hc125.pdf?HQS=TI-null-null-alldatasheets-df-pf-SEP-wwe&ts=1657406551947&ref_url=https%253A%252F%252Fwww.alldatasheet.com%252Fdatasheet-pdf%252Fpdf%252F797403%252FTI1%252FSN74HC125N.html)に見つかる。

### ピンアサイン
トライステートバッファ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/2c80491b-d37b-9585-7e21-593c2400147f.png", width=60%>
OEにより制御（ハイインピーダンスをつくるかどうか）する。

NOT
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/de79bfb6-3c60-d4dd-de07-db36391dc0b5.png", width=50%>

### ソースコード
```c++
#define CONTROL 7
bool tgl;

void setup() {
  tgl = false;
  Serial.begin(9600);
  pinMode(CONTROL, OUTPUT);
  digitalWrite(CONTROL, ((tgl == true)? HIGH: LOW));
}

void loop() {
  if (Serial.available() > 0) {
    (void)Serial.readStringUntil('\n');
    tgl = !tgl; Serial.println(tgl);
    digitalWrite(CONTROL, ((tgl == true)? HIGH: LOW));
  }
}
```
PCからのシリアル入力があると、変数tglを反転させ、その状態をGPIOピンに反映させる、単純なもの。

### 実験
中央にあるICがトライステートバッファ、その右にあるICがNOTである。ここでは、GPIOからのHigh出力があるときに右下のLEDを点灯させることとした。下記写真は、GPIO出力Lowのときのもの。左上のLEDが点灯する。
![IMG_20220710_104434.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/06b0c5d9-6382-a10a-997d-ef33d880e802.jpeg)
下記写真は、GPIO出力Highのときのもの。左下のLEDが点灯する。
![IMG_20220710_104457.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/640b293a-7e54-285d-4a48-7d5071a66a0b.jpeg)
単純ではあるがわかりやすい。

EOF
SwiftUI-Listの背景色の変更(iOS16~)

# はじめに
SwiftUIのList,
とても便利で、皆さんよく使用されると思いますが、Listの背景色を変えるのが、ひと手間かかっていました。
(UITableView.appearance()で色を変更していた)

今回、iOS16からの対応で、scrollContentBackground(_:)を使用する事で、簡単に設定できるようになりましたので、紹介したいと思います😀

https://developer.apple.com/documentation/swiftui/text/scrollcontentbackground(_:)

### サンプルAPP
```swift
import SwiftUI

struct ContentView: View {

    var body: some View {
        List{
            Section("sample"){
                Text("A")
                Text("B")
            }
        }
        // Listの背景色を無効化
        .scrollContentBackground(.hidden)
        // 色の指定
        .background(Color.red)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```
![スクリーンショット 2022-10-15 7.53.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2428088/1fb7d219-76fe-d27b-4203-87280aaa3006.png)
VPCエンドポイントを作成して、S3にアクセスする

AWSマネジメントコンソールにログインしてください。
VPCダッシュボードを表示してください。
[エンドポイント]をクリックしてください。
![スクリーンショット 2022-10-15 6.59.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/f36cb1be-e3ec-227e-265b-e957683fc629.png)

エンドポイントページが表示されます。
[エンドポイントを作成]をクリックしてください。
![スクリーンショット 2022-10-15 7.00.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/f4e0417a-5f65-811c-bcab-84bf0f44f8b2.png)

エンドポイントを作成ページが表示されます。
[名前]にお好きな名前を入力してください。
[サービスカテゴリ]は、「AWSのサービス」を選択してください。
![スクリーンショット 2022-10-15 7.03.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/5563a038-50a1-8b98-ac3e-9b5440526415.png)

[サービス]で、「S3」と入力し、検索結果から、Gateway型を選択します。![スクリーンショット 2022-10-15 7.07.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/a016e072-26ca-1614-85b3-23dcdb07aec5.png)

[VPC]では、VPCエンドポイントを作成するVPCを選択してください。
[ルートテーブル]では、対象のサブネットのルートテーブルを選択してください。
最後に、[エンドポイントを作成]をクリックしてください。
![スクリーンショット 2022-10-15 7.05.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14202/b0403aab-3352-093c-c41b-78da6e5d501a.png)

VPCエンドポイントが作成されました。
プライベートサブネットに作成したEC2インスタンスにログインし、以下のコマンドを実行してください。
(リージョンを指定しないと、タイムアウトになることがあります。)
s3のバケットのリストが表示されたら、無事にアクセスできています。
EC2インスタンスは、S3へのアクセスを許可するロールを付与しておいてください。
```
$ aws s3 ls --region ap-northeast-1
```

以上です。
[GIMP]ツールボックスを旧バージョンの表示形式に戻す手順

# 概要
GIMP最新版ではツールボックスがコンパクトにまとまって表示されるようになっています。
![スクリーンショット 2022-10-15 6.44.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/3bc960a6-9381-0791-ba4e-bd09f2c6b55a.png)
ですが、以前の表示形式の方が良いという方もいると思います。
![スクリーンショット 2022-10-15 6.47.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/76a9cc4d-53be-35bc-260e-b1550a92bb03.png)

# 本文
画面上部メニューからGIMP > 設定（Windows版は 編集 > 設定）
![スクリーンショット 2022-10-15 6.43.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/3896ca68-10c6-47ba-7783-1c6d6a23328f.png)
ポップアップした設定画面でツールボックス > ツールボックスのアイコンをグループ化する
このチェックボックスをOFFにすると以前の表示形式に戻すことができます。
![スクリーンショット 2022-10-15 6.44.31 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/fcf9dd49-86d6-922e-4633-eb20b2ccafd5.png)
chromのエラーについて

NET::ERR_CERT_AUTHORITY_INVALID エラー
+ SSL サーバー テストの実行
+ 有効な機関から証明書を取得する
+ SSL 証明書を更新する
+ ページをリロードしてみてください（またはシークレットモードを使用してください）
+ ブラウザのキャッシュと Cookie をクリアする
+ コンピューターの時計を同期する
+ コンピューターの SSLでない状態からアクセスする（エラーメッセージの下の詳細設定ボタンからはいる）
ボックスモデル

# ボックスモデルとは
![スクリーンショット 2022-10-15 4.04.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/11220a3d-b288-a187-4499-333a45779dbd.png)

# マージンの指定
```CSS
/* 上下左右に同じ値のマージンを指定 */
  margin: 10px;

  /* 上下左右別々に指定する場合 */
  /* 上 */
  margin-top: 10px; 
  /* 下 */
  margin-bottom: 10px;
  /* 左 */
  margin-left: 10px;
  /* 右 */
  margin-right: 10px;

  /* 上記の記述と同様の結果が得られる （上、右、下、左の順*/
  margin: 10px 10px 10px 10px;

  /* 上下の値と左右の値を同じにしたい場合 （上下、左右の順番）*/
  margin: 10px 10px;
```

# マージンの相殺

- 例えばh1要素にマージンボトム100pxを指定、p要素にマージントッp50pxを指定した場合、マージンは150pxにはならず相殺されて100pxになる。
- 左右のマージンは相殺されない
- パディングやボーダーなどは上下左右すべて、相殺されない。
- 親要素と子要素の上下のマージンは相殺される
![スクリーンショット 2022-10-15 4.05.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/ff106c0a-57c7-aee8-cdce-47635a2b08fd.png)


# パディングの指定
- 基本的にはマージンと同様
```CSS
/* 上下左右に同じ値のマージンを指定 */
  padding: 10px;

  /* 上下左右別々に指定する場合 */
  /* 上 */
  padding-top: 10px; 
  /* 下 */
  padding-bottom: 10px;
  /* 左 */
  padding-left: 10px;
  /* 右 */
  padding-right: 10px;

  /* 上記の記述と同様の結果が得られる （上、右、下、左の順*/
  padding: 10px 10px 10px 10px;

  /* 上下の値と左右の値を同じにしたい場合 （上下、左右の順番）*/
  padding: 10px 10px;
```

# ボーダーの指定
```CSS
/* 基本的な記述方法 */
  border: 線の種類 太さ 色;
  border: solid 10px #fff;

  /* 上記と同様の結果が得られる記述 */
  border-style: solid;
  border-width: 10px;
  border-color: #fff;

  /* 上部にだけボーダーを付ける(topの部分をbottom,leftなどに変更して下や左などに指定できる) */
  border-top: solid 10px #fff;

  /* 上記と同様の結果を得られる記述 */
  border-top-style: solid;
  border-top-width: 10px;
  border-top-color: #fff;
```

# ボックスの横幅と高さを指定
```CSS
/* 横幅100px,高さ200pxのボックスを作成 */
  width: 100px;
  height: 200px;

  /* ボックスを中央に表示したい場合はautoを使う */
  margin-left: auto;
  margin-right: auto;

  /* コンテンツをボックス内で中央揃えする */
  text-align: center;
```

# マージンとパディングの違い
- マージンはボーダーの外側、パディングはボーダーの内側に余白ができる
- マージンは負の値を使用できる
- マージンは値にautoを使用できる
CSSの基本

# コメントアウト
```CSS
/*コメントアウト*/
```
- ショートカットキー(mac)・・・コメントアウトしたい箇所を指定しcommand+/

# 色の指定
```CSS
p {
  color: #ffffff
}
```
[MDN:color]https://developer.mozilla.org/ja/docs/Web/CSS/color_value

# 背景の指定
```CSS:色の指定
P {
  background-color: #ffffff;
}
```
```CSS:背景画像指定
background-image: url(パス);

#画像を繰り返す場合
background-repeat: no-repeat;

#中央に持ってきたい場合
background-position: center;
```
- img要素との使い分け
    - image・・・重要な画像
    - background-image・・・背景画像
フォームについて

# フォームとは
- ユーザーがウェブページからデータをサーバに送信するためのもの

# form要素
```HTML:forn要素
<form action="#" method="POST">
</form>
```
- action属性・・・どこにデータを送信するのかを記述する
- method属性・・・HTTPリクエストメソッドの種類を記述

# HTTPメソッドの種類
- GET・・・サーバからデータを取得する場合に使用

- POST・・・サーバへデータを送信する場合に使用

# input要素
```HTML:input要素
<input type="text">
```
- データを入力するフォームの部分を作成する要素
- type属性を指定することでいろいろなフォームを作成可能

- inputの型
[MDN]https://developer.mozilla.org/ja/docs/Web/HTML/Element/input

# 単一行のテキスト入力欄の作成
![スクリーンショット 2022-10-15 3.32.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/ea430312-fc85-ce11-9e1f-69012c41bc76.png)

```HTML:テキスト入力欄の作成
<dt>お名前</dt>
<dd><input type="text" name="username"></dd>

<dt>メールアドレス</dt>
<dd><input type="text" name="email"></dd>
```

# プルダウンメニューの作成
![スクリーンショット 2022-10-15 3.34.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/4b6bf69b-42e9-becc-a60a-9dded505d97a.png)

```HTML:プルダウン
<select name="area">
  <option value="" selected>選択してください</option>
  <option value="hokkaido">北海道</option>
  <option value="okinawa">沖縄</option>
</select>
```
- selectedを指定することで、選択肢が初期状態で選択される
- select要素内で１つのオプションだけがselected属性を持てる

[MDN:select]https://developer.mozilla.org/ja/docs/Web/HTML/Element/select
[MDN:option]https://developer.mozilla.org/ja/docs/Web/HTML/Element/option

# ラジオボタン
![スクリーンショット 2022-10-15 3.59.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/682f14bd-6324-ae5a-9601-a3baeaafe9d1.png)


```HTML:ラジオボタン
<dt>性別</dt>
<dd>
<input type="radio" name="gender" value="male" checked>男性
<input type="radio" name="gender" value="female">女性
</dd>
```
- name属性には同じ名前をつける
- checkedをつけると初期状態で選択されるようになる

[MDN:input]https://developer.mozilla.org/ja/docs/Web/HTML/Element/input

# テキストエリア
![スクリーンショット 2022-10-15 4.00.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/93ad7575-754a-d432-d262-3a3a3a5f4213.png)


```HTML:テキストエリア
<dt>お問い合わせ内容</dt>
<dd><textarea name="contents"></textarea></dd>
```

[MDN:textarea]https://developer.mozilla.org/ja/docs/Web/HTML/Element/textarea

# チェックボックス
![スクリーンショット 2022-10-15 4.00.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/3872ab60-7dbe-cd54-bb24-90d766a303b2.png)


```HTML:チェックボックス
<dt>プライバシポリシーに同意する</dt>
<dd><input type="checkbox" name="privacy" value="1"></dd>
```
- 初期状態でチェックを付けたい場合はcheckedをつける

[MDN:checkbox]https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/checkbox

# 送信ボタン
![スクリーンショット 2022-10-15 4.01.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/43abb1a0-c769-f2f2-c965-76bf78420c0a.png)


```HTNL:送信ボタン
<input type="submit" name="submit" value="送信">
```
- form属性のアクション属性に指定したパスにデータが送られる

# ラベル
- ラベルをクリックしても選択が可能になる
- ラベルを付けたい要素にidをつけることでラベルを付与できる

```HTML:ラベル
<dt><label for="username">お名前</label></dt>
<dd><input id="username" type="text" name="username"></dd>
```
テーブルについて

# テーブル
![スクリーンショット 2022-10-15 1.17.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/b06fb1d9-bfd6-1193-edb3-275251294494.png)

# tr要素
- テーブルの１行を定義する
- 行は横方向

# td要素
- テーブルデータが入るセルを定義する
[MDN]https://developer.mozilla.org/ja/docs/Web/HTML/Element/td

# th要素
- テーブルのヘッダーを定義
```html
  <body>
    <table border="1">
      <tr>
        <th>性</th>
        <th>名</th>
      </tr>
    </table>
  </body>
```
![スクリーンショット 2022-10-15 3.00.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/0aec4e08-7dca-01f5-380b-6c47dfd53884.png)


# thead要素
![スクリーンショット 2022-10-15 3.05.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/1600cf5a-c839-14de-61a1-b1739312a252.png)


- テーブルのヘッダー行を定義
[MDN]https://developer.mozilla.org/ja/docs/Web/HTML/Element/thead

# tbody要素
- テーブルのボディ部分を定義する

# tfoot
- テーブルのフッター行を定義する

# キャプション
- テーブルのタイトル
- テーブル要素の最初の子要素として配置する
```html
  <body>
    <table border="1">
      <caption>キャプション</caption>
      <tr>
        <td>データ１</td>
        <td>データ２</td>
        <td>データ３</td>
      </tr>
    </table>
  </body>
```
![スクリーンショット 2022-10-15 3.10.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/3bce7593-653f-4db6-03f9-de8e10341ee3.png)


# セルの結合
- 横に結合する場合
- colspan属性を使用
```html
  <body>
    <table border="1">
      <tr>
        <th colspan="3">セルを結合</th>
      </tr>
      <tr>
        <td>コンテンツ１</td>
        <td>コンテンツ２</td>
        <td>コンテンツ３</td>
      </tr>
    </table>
  </body>
```
![スクリーンショット 2022-10-15 3.24.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/e7d05078-ac26-dc55-b99f-99eb9c2fa336.png)




- 縦に結合する場合
- rowspan属性を使用
```html
  <body>
    <table border="1">
      <tr>
        <th rowspan="3">セルを結合</th>
        <td>コンテンツ１</td>
      </tr>
      <tr>
        <td>コンテンツ２</td>
      </tr>
      <tr>
        <td>コンテンツ３</td>
      </tr>
    </table>
  </body>
```
![スクリーンショット 2022-10-15 3.24.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2904183/abb0a710-5494-9637-2840-364ce5dacac5.png)
Terraformインストールとtfenvでバージョン管理

## １．tfenvのインストール
### githubからcloneしてくる。</font>
```
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
```
### .bash_profileにPATHを追加し反映する。
```
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
```
## ２．terraformのインストール
### ２通りの方法を記載します。
#### &emsp;１．HashicorpからZIPをダウンロードする方法
#### &emsp;&emsp;hashicorpからzipをダウンロードし解凍後、/usr/local/bin/に移す。
```
sudo curl https://releases.hashicorp.com/terraform/1.2.0/terraform_1.2.0_linux_amd64.zip -o terra.zip
unzip terra.zip -d /usr/local/bin/
```
#### &emsp;&emsp;terraformコマンドが機能するのを確認する。
```
terraform --version
Terraform v1.2.0
on linux_amd64
```
### &nbsp;２．yumレポジトリにhashicorp.repoを追加しyumでインストール方法
#### &emsp;&emsp;yumのレポジトリ管理にはyum-config-managerコマンドを使うと便利です。
#### &emsp;&emsp;それにはyum-utilsがインストールされている事が前提です。

```
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform
```
## ３．Terraformのバージョン指定
#### インストール可能なバージョン確認
```
tfenv list-remote
```
#### バージョンを指定してインストール
```
tfenv install 1.3.2
```
#### インストール済みのバージョン確認
```
tfenv list
* 1.3.2 (set by /home/ec2-user/.tfenv/version)
```
#### バージョンの切り替え
```
tfenv use 1.3.2
```
tfenvツールでTerraformのバージョン管理

## １．tfenvのインストール
githubからcloneしてくる。
```
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
```
.bash_profileにPATHを追加し反映する。
```
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
```
## ２．terraformのインストール
２通りの方法を記載します。
１．HashicorpからZIPをダウンロードする方法
hashicorpからzipをダウンロードし解凍後、/usr/local/bin/に移す。
```
sudo curl https://releases.hashicorp.com/terraform/1.2.0/terraform_1.2.0_linux_amd64.zip -o terra.zip
unzip terra.zip -d /usr/local/bin/
```
terraformコマンドが機能するのを確認する。
```
terraform --version
Terraform v1.2.0
on linux_amd64
```
２．yumレポジトリにhashicorp.repoを追加しyumでインストール方法
yumのレポジトリ管理にはyum-config-managerコマンドを使うと便利です。
それにはyum-utilsがインストールされている事が前提です。

```
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform
```
一次元配列の値が他の連想配列の中に全て存在するかチェックする方法

はじめに
--
reactで実装しているいて、配列を追加、削除、変更したり値が存在しているかチェックしたりします。
その中で、ある一次元配列の値が連想配列の中に全て存在するかチェックする方法がネットで見当たらず時間が少しかかったので備忘録として残します。
（ベストな書き方ではないかもですが。。)

everyメソッドとsomeメソッドを使用
--
everyメソッドは、配列内の値全てがメソッド内の条件式でtrueの場合にtrueを返してくれます。
そしてsomeメソッドは、配列内の値の一つでもメソッド内の条件式でtrueの場合にtrueを返してくれます。
この二つのメソッドを使用することで下記に一次元配列の値が連想配列内に存在するかチェックすることができます。

~~~react
fruits = ["apple", "lemon", "banana"]

fruitsMenuExpectTrue = [
  {fruitName: "apple", price: 200},
  {fruitName: "banana", price: 100},
  {fruitName: "lemon", price: 50}
]

fruitsMenuExpectFalse = [
  {fruitName: "grape", price: 200},
  {fruitName: "banana", price: 100},
  {fruitName: "lemon", price: 50}
]

//fruits配列の値が、fruitsMenuExpectTrueの配列内に全て存在しているか一つづつチェックしてくれる
fruits.every(fruit => fruitsMenuExpectTrue.some(Menu => Menu.fruitName === fruit)
//=> trueを返す
fruits.every(fruit => fruitsMenuExpectFalse.some(Menu => Menu.fruitName === fruit)
//=> falseを返す
~~~

終わりに
--
上記の様な記述で配列に値の有無を判断して、どの処理を走らせるか判断しています。
ただ使うメソッドが増えすぎたり、配列名が長いと地獄的に読みづらいコードになってしまうので気をつけないとなと思います。
構造体（Struct）について死ぬほど簡単にまとめてみた #1

## はじめに
どの記事・参考書よりも短く簡潔に触れていこうと思うので、めっちゃ詳しく勉強したい人はこの記事全く参考になりません。

## 構造体とは
構造体とは設計図であり、インスタンス化（PCのメモリに突っ込む）をして初めて使用することができる。また、インスタンス化とは実体化ともいう。
上記を踏まえて、超大まかに要点だけ絞っていうと二つにまとめることができる。
1. プロパティ（変数や定数）、メソッド（関数）、イニシャライザ（初期化関数）をひとまとめにしたもの
2. 入力（引数）、出力（戻り値）の役割を担うもの


## 詳細
・ インスタンス化をしてみよう
インスタンス化のやり方は主に２種類ある。
（１）構造体名（）
（２）変数 or 定数 = 構造体名（）　←変数は構造体のインスタンスという
```swift:Swift
struct Apple {
    let kind = "ふじ"
    var size = "S"
}

//インスタンス化１
Apple().kind //ふじ
Apple().size //S

//インスタンス化２
var apple = Apple()
print(apple.kind) //ふじ
print(apple.size) //S
apple.size = "L" // 書き換えも可能である
}
```

- イニシャライザを使って初期値を決めてみよう
Swiftでは原則プロパティの初期値は決まっていなければならず、それを解決させましょうみたいなのがイニシャライザ。また、インスタンス化時に実行される特殊なメソッド。

```swift:Swift
struct Students {
    //構造体のプロパティにおいては初期値がなくても良い
    var name: String
    var age: Int
    var from: String

    init(n: String, a: Int, f: String){
        self.name = n
        self.age = a
        self.from = f
    }
}

//インスタンス化時に初期値を決める
var students = Students(n: "太郎", a: 10, f: "東京")
print(students.name, students.age, students.from) //太郎, 10, 東京

//おまけ
//メンバワイズイニシャライザー（initを使わなかった場合のデフォルトで備わっているイニシャライザ）
var students1 = Students(name: "小太郎", age: 5, from: "神奈川")
print(students.name, students.age, students.from) //小太郎, 5, 神奈川
```

・メソッドを使ってみよう
```swift:Swift
struct Algebra {
    var a: Float
    var b: Float
    
    //メソッド
    //和
    func sum() -> Float {
        return a + b
    }
    
    //差
    func diff() -> Float {
        return a - b
    }
    
    //積
    func times() -> Float {
        return a * b
    }
    
    //商
    func div() -> Float {
        return a / b
    }
}

//インスタンス化
let algebra = Algebra(a: 1.0, b: 2.0)
algebra.sum() //以下、各々値が出る
algebra.div()
algebra.times()
algebra.diff()
```


## おわりに
まとめるの疲れました。

## 開発環境
- Xcode-13.4.1
- iOS13以降
HTMLの基礎知識（コメント機能で動画を投稿）

みなさんこんにちは！！
今回はYouTude投稿とコメント機能の応用した機能になります！！

<br>**STEP１：モデルを作成しよう！**

```ruby:ターミナル
rails g model Youtubecomment content:string user:references register:references
rails generate migration AddYoutubeUrlToRegisters youtube_url:string
rails db:migrate
```

**Step4: helperにメソッドの定義**
```ruby:helpers/registersHelper
module RegistersHelper
  def find_youtube_url(youtube_url)
    if youtube_url[0..16] == "https://youtu.be/"
      return youtube_url[17..27]
      # "https://youtu.be/WGiUk8VakxQ" 11桁のyoutubeのURLが出力されるようにする
    else
      return youtube_url[32..42]
      # "https://www.youtube.com/watch?v=WGiUk8VakxQ" 11桁のyoutubeのURLが出力されるようにする
    end
  end
end
```
**STEP2：アソシエーションを組み**

```ruby:app/models/user.rb
has_many :youtubecomments, dependent: :destroy
```

```ruby:app/models/register.rb
has_many :youtubecomments, dependent: :destroy
```

**STEP3：コントローラーの作成**
```ruby:ターミナル
rails g controller youtubecomments
```

**STEP4：コントローラーの編集**
```ruby:app/controllers/youtubecomments_controller.rb
class YoutubecommentsController < ApplicationController
  before_action :authenticate_user!

  def create
   @register = Register.find(params[:register_id])
    youtubecomment = @register.youtubecomments.build(youtubecomment_params) #buildを使い、contentとtweet_idの二つを同時に代入
    youtubecomment.user_id = current_user.id
    if youtubecomment.save
      flash[:success] = "コメントしました"
      redirect_back(fallback_location: root_path)
    else
      flash[:success] = "コメントできませんでした"
      redirect_back(fallback_location: root_path)
    end
  end

  private

    def youtubecomment_params
      params.require(:youtubecomment).permit(:content)
    end
end
```

```ruby:app/controllers/registers_controller.rb
#　省略
def show
        @register = Register.find(params[:id])
# 追記ここから
        @youtubecomments = @register.youtubecomments
        @youtubecomment = Youtubecomment.new
# 追記ここまで
    end
#省略
private
    def register_params
        params.require(:register).permit(:youtube_url)
    end
```


**STEP5：ルーティング**
```ruby:routes.rb
#追記箇所
    resources :registers do
   #追加
    resources :youtubecomments, only: [:create] 
   # ここまで
    end
```

**STEP6：Viewsの設定**
```ruby:app/views/registers/show.html.erb
# 追記ここから
<div class="comment-wrapper">
  <p>コメント一覧</p>
  <% @youtubecomments.each do |c| %>
    <div>
      <%= c.user.email unless c.user.blank? %>
      <br>
      <%= c.content %>
      <br>
       <iframe width="560" height="315" src="https://www.youtube.com/embed/<%= find_youtube_url(@register.youtube_url) %>" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
    </div>
      <br>
  <% end %>

  <% if user_signed_in? %>
    <%= form_with(model: [@register, @youtubecomment], local: true) do |f| %>
      <%= f.text_area :content %>
      <%= button_tag type: "submit" do %>
        <i class="far fa-comments"></i> コメントする
      <% end %>
    <% end %>
  <% end %>
</div>
#追記ここまで
```
以上です！！
少し長かったですね！
お疲れ様でした！！
ルワンダでのオフショア開発を足掛かりに成長市場のアフリカでソフト企業を立上げ成長させてく話1

#### 自己紹介

1エンジニアとしてQiitaの記事に大変お世話になってきていますが、ここに至って初めて投稿しますhidetanaと申します、よろしくお願いします。
レックスバート・コミュニケーションズ株式会社( https://www.rexvirt.com/ ) の代表をやっておりますが、現役で開発もやっています。現場に委譲せねばという経営あるべき論と、エンジニアとして現場から離れたくない葛藤のなか生きております。

#### この記事について

何回かに分けて書いていく予定ですが、単にルワンダのオフショアだけの話ではなく、地理的に離れた場所でどのような開発体制を構築し、プロジェクトをこなしていっているかや、海外での開発を単にアウトソース先としてではなく、その先にある市場を一緒に目指していくような話、その中で日本がどのような強みを出せるのかなど、思いつくままに話は脱線するかもしれませんが、そういった話を書いていきます。


#### 大まかな流れ

1. ルワンダを紹介され、オフショアやってみたら、なかなかイイ感じだった。
1. お国の力を借りた次の一手。
1. ルワンダに会社設立。
1. 案件をこなしながら自分たちの開発体制を作った。
1. 留学とインターン受け入れからの人材育成の流れ。
1. 自社サービスの開発。
1. ルワンダ国内での認知度と大型案件。
1. アメリカやヨーロッパからの案件。
1. 自社サービスが売れ始める。
1. 自社の採用に直結するスクールを立ち上げる。＜今ココ
1. 目指す将来について



#### さて、皆様はオフショア開発やっていますか？
古くは中国やインドが日本の主なオフショア先でしたが、人件費上昇の問題などもあり、近年はベトナムを中心とした東南アジアでのオフショアが中心かと思います。東欧も聞きますね。

そんな中、私は __アフリカ__ の __ルワンダ共和国__ というところで、足掛け10年、オフショア開発をやっています。
なぜルワンダでオフショア開発を？という質問には、もう何千回と答えてきたような気がします。
正直説明に疲れました。ので、やはりちゃんとアウトプットして、そこを見てもらえるようにしないとな、ということで書き始めています。


![RwandaPos.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41753/70171803-f0fc-72ae-1c8c-c84780566e33.png)



#### なぜアフリカでオフショアを！？

前置きが長くなりましたが、本題です。


__実は偶然です。__




で、終わったらダメですよね。
きっかけは、私が独立起業した直後、実質的にはフリーランスとして、スタートアップのCTOをしたり、SESとして自分自身を企業に送り込んだりしておりましたが、その中のあるスタートアップの（~~潰れました~~）代表から、

__「ルワンダからコーヒーを仕入れている人と知り合ったんだけど、ルワンダにもエンジニアが居るらしいよ？めっちゃ安くアウトソースできるだろうから会ってみない？」__

こんな感じのことを言われたのが、すべての始まりでした。
(ちなみに先に言っておくとアフリカの人件費は安くはないです。）

今から11年ぐらい前になる、2011年の話です。

大体の場合、その時点でNoになると思うのですが、私は誰もやってなさそうなことに興味を惹かれ、みんなが右向いていたら自分は左に、と進んでしまう昔からの性格であるため（スーパーファミコンではなく、メガドライブを買ってしまったことに端を発するメンタリティ）、まずは会って話を聞くことにしました。
ルワンダのコーヒーを仕入れている方は技術的なことはわからない方だったので、とにかくまずはそのエンジニアとやらと紹介してくれとお願いしまして、繋いでもらいました。

この時繋がったルワンダのエンジニアが、結果として今現在に至る最重要ビジネスパートナーであり、一緒に事業を作ってきた人物である、Alain Kajangwe氏でした。

早速、その時手伝っていたスタートアップで必要なちょっとしたスマホアプリの作成を依頼しました。
彼はスマートフォンのアプリ開発は実践としては経験がなかったですが、とにかくキャッチアップしてやってみるということで、こちらとしても失敗しても自分たちでやり直せる範囲だったので、発注してみました。

当時私は、仕事上英語には日々触れていたものの、聞く話すには全く自信がなく、ひたすらメールやチャットでのやり取りになりましたが、アプリは無事に完成し、リリースすることが出来ました。

この時、当然アプリ開発をするため、こちらとしては仕様書などを作り、それを説明し、実装～テストとやっていくわけですが、こちらからの質問や要望への回答はタイムリーかつ適切だし、逆に仕様として伝えきれない部分はしっかり質問して来てくれて実装に反映してくれ、テストの段階でも細かいバグの修正や、UI改善のための細かいリクエストにも随時対応してくれて、１～２カ月の期間でしたが、非常にスムーズに完成に持って行けました。

私個人の経験として、過去に別のオフショアなども経験していましたが、その時の苦い経験から、オフショアなど無理派でしたが、この経験で、相手がしっかりしてるなら全然いけるじゃない！に変わった瞬間でした。


こうなってくると当然次の興味が出てきます。

__ルワンダってこんなエンジニアがゴロゴロしてるの！？単価こんなに安く？すごくない？__



・・・・話しはそんなに単純ではなく、ここが長きにわたるチャレンジのスタート地点となるわけですが、この時点ではこれは掘り出し物を見つけた！ぐらいの感覚でした。






#### お国の力を借りた次の一手

と、ここまでは順調だったものの、会社として一旦アウトソースできるものはしてしまった状態でした。現地側も現地の会社にお願いしたというよりルワンダのフリーランスエンジニアに依頼した形だったので、組織的に開発を委託する体制を作るというのも難しい状態でした。
そんな中、ルワンダを紹介してくれた方が、日本貿易振興機構(JETRO)に相談に行ってはどうか、と勧めていただき、打ち合わせをセッティングして頂きました。
恥ずかしながら、国の機関とかまったく絡まない人生を送ってきたため、JETROだJICAだと言われても何それ？状態でした。

とはいえ、言われるがままJETROさんを訪れ、その後、ある事業に採択されたことで、流れは次のフェーズに入っていくこととなりました。




### 次回

長くなってきましたので、この続きは次の記事にします。
次回は、開発輸入実証事業に採択されのち、初めてルワンダに降り立ったこととか、その中で取り組んだことなどを紹介できればと思います。


### 追記

第2回はこちら


https://qiita.com/hidetana/items/af05a3a3c3d5a43fce30
成果物を作る為の計画

## 成果物を作る為の下準備


### 自問自答

- Q,　どの様な成果物を作りたいのか？ 
　
 A,　自分の経歴や自己紹介、現状のスキルなどを
　　テキストや動画を使って説明出来るアプリの作成したい


- Q,　どの様な言語で？なぜその言語なのか？

  A, 　 言語はRubyでフレームワークはRubyと相性の良いRuby on Railsを
　    　 使おうと思います.プログラミングスクールの課題でRubyを扱っていて  
     　　  多少の知識はあるのでRubyをある程度マスターしてから他の言語も学習
     　　  していきたいと思っています。
　　 
- Q, なぜRubyで成果物を作る必要があるのか？

  A, 　プログラミングスクールではRubyの基礎の知識とどの様に進めていけば
　　作りたいものが作れるかのプロセスを学んだので今回は自分で全て考えてタスクを
　　言語化＋細分化し作成することでRubyの言語を理解出来ている事を証明し転職活動時
　　のポートフォリオとして使いたい


- Q,どの様な順序で進めていけば効率良く作業できるか？

  　 ゴールを設定する（11月末まで）46日間
  　 1日の作業量を最低3時間は確保する
  　 詰まったら即質問（MENTA）
  　 やらないことを洗い出す（読書や成果物に完成のない知識の収集）
Progate Javascript すべて終了。

<h1>やっと"プログラミング"のような内容</h1>
アロー関数に引数、因数を受け取る関数の呼び出しに戻り値、クラス・オブジェクトからインスタンス、コンストラクタ、メソッド等ざっと一通り基礎の基礎が終わった。（ファイルの受け渡しもやった）
<br>考え方や概念に関してざっと理解しただけなのでどんどん実践を通して理解していきたいと感じる。来月にはまともなwebサイト作れるようになりたいのでサーバーの作成>>webアプリ制作とprogateの内容を進めて全体像をつかんでいきたいと思う。楽しみがいっぱい。

<h3>ペースがやや遅い</h3>
jsの内容も２日あれば終わらせることができたのでどんどん進めていくよう努める。
<br>
Dockerfileでコンテナレジストリのイメージからファイルを取得する方法


# コンテナイメージを作る

Dockerfileを使用して例えばJavaアプリケーションのイメージを作るときは

```dockerfile
FROM myregistry.azurecr.io/jdk:latest
COPY app.1.0.jar /myapp/app.jar
```

のような感じで、事前にMavenなんかでビルドされたjarファイルをコピーして
```bash
/kaniko/executor -f /myapp/Dockerfile -c /myapp --destination=myregistry.azurecr.io/myapp:latest
```
とすると`myregistry.azurecr.io/myapp:latest`ができます。

# コンテナイメージにあるjarファイルを再利用

次に、新しいバージョン`myregistry.azurecr.io/myapp:2.0.1`を作りたいが、jarファイルは先ほどの`app.1.0.jar`をそのまま使用したい場合どうすれば良いか。
`app.1.0.jar`を何処かに保存しておけばそれをコピーすればいいだけですが、それ以外にも`myregistry.azurecr.io/myapp:latest`からコピーすることもできます。

```new.dockerfile
FROM myregistry.azurecr.io/jdk:latest
COPY --from=myregistry.azurecr.io/myapp:latest /myapp/app.jar /myapp/app.jar
```
```bash
/kaniko/executor -f /myapp/new.Dockerfile -c /myapp --destination=myregistry.azurecr.io/myapp:2.0.1
```

こうすれば、例えばベースイメージの`myregistry.azurecr.io/jdk:latest`でセキュリティーのパッチなどをしないといけない場合、ベースイメージだけアップデートしてJavaアプリケーションを変更せずに新しいイメージが作成できます。

# Dockerfileに変数でコンテナイメージのバージョンを指定

次に、変数を使って任意のイメージからファイルをコピーする場合どうすれば良いか。
先ほどは`myregistry.azurecr.io/myapp:latest`からjarファイルをコピーしました。CI/CDでパイプラン上で指定された任意のバージョンからファイルをコピーする場合、Dockerfileで変数を使います。

```bash
/kaniko/executor -f /myapp/new.Dockerfile -c /myapp  --build-arg "VERSION=${VERSION}" --destination=myregistry.azurecr.io/myapp:${NEW_VERSION}
```
まず、kanikoでコピー元のイメージのバージョンを変数`"VERSION=${VERSION}"`としてCI/CDなどで指定します。新しく作るイメージのバージョンは`"VERSION=${NEW_VERSION}"`とします。

```new.dockerfile
ARG VERSION
FROM myregistry.azurecr.io/myapp:${VERSION} as myimage

FROM myregistry.azurecr.io/jdk:latest
COPY --from=myimage /myapp/app.jar /myapp/app.jar
```
ここではマルチステージビルドを使用して、まず`myregistry.azurecr.io/myapp:${VERSION} as myimage`で`myimage`をステージとして指定します。
次に、`myimage`からjarファイルをコピーします。

変数を使う場合、先ほどのようにマルチステージビルドを使用せずに`COPY --from=myregistry.azurecr.io/myapp:${VERSION}`とすることはできません。一度ステージを作ってそこで変数を受け取る必要があります。

kanikoの実行結果は以下の感じです。
```bash
VERSION=2.0.1
NEW_VERSION=2.1.1
```

```
Resolved base name myregistry.azurecr.io/myapp:2.0.1 to myimage 
Retrieving image manifest myregistry.azurecr.io/myapp:2.0.1 
Retrieving image myregistry.azurecr.io/myapp:2.0.1 from registry myregistry.azurecr.io 
...
...
Built cross stage deps: map[0:[/myapp/app.jarr]] 
Saving file myapp/app.jar for later use 
...
...
COPY --from=myimage /myapp/app.jar /myapp/app.jar
Pushing image to myregistry.azurecr.io/myapp:2.1.1
```

このようにすると、コンテナレジストリ`myregistry.azurecr.io/myapp`上にあるどのバージョンからもファイルを取り出して新しいイメージを作ることができます。

アプリケーションのコード変更はないがベースイメージのライブラリなどを変更したい場合に有効な手段です。
M1 Macで始めるハッカー生活 （Hack The Box チュートリアル）

# M1 Macで始めるハッカー生活 （Hack The Box チュートリアル）

## Hack The Boxとは
サイバーセキュリティのスキル向上のトレーニングができるオンラインプラットフォームです。https://www.hackthebox.com/
ここではゲームのようにレベルをあげながらペネトレーションテストのスキルを向上させることができます。この記事ではHack The Box（以下HTB)での学習に必要な環境構築をします。というのも、HTBではHTBが用意したペネトレイト対象のマシンにVPNで接続して実際に攻撃を行う形で学習を進めていきます。そして手元にはKali Linuxを用意するのが一般的です。M1Mac上でVMを動かすのはなかなか面倒なので、その参考になればと思います。

## 環境構築
参考記事（https://qiita.com/aka_hat/items/9df05ee63ed609dedfb3）
上記参考記事のうち、
### Kali Linuxのダウンロード
arm版のKali Linuxのisoをダウンロードしてください。
### UTMのインストール、起動
UTMの公式サイトにてUTMをダウンロードしてインストールしてください。インストールが完了しましたら起動してください。
### UTMのVM設定
- __Create a New Virtual Machine__ ボタンを押下
- __Virtualize__ を選択し、__Linux__ を選択
- __Brouse__ を押して先程ダウンロードしたisoファイルを選択
- 適宜メモリサイズとストレージサイズを設定
- 名前をつけてSave

ここまでは参考記事と同じです。ここでVMを起動する前に、VMの __Edit Selected VM__ を開きます。その中の __Display__ の __Type__ を __Console Only__　に変更します。
そしてここからまた参考記事と同じくVMを起動してインストールを進めていきます。ただし参考記事のようなGUIでのインストールとは異なります。
- VMを起動しInstallを選択する（__Display__ の __Type__ を __Console Only__　に変更していない場合、ここでブラックスクリーンが続いてインストールが進まなくなります）

あとはよくあるLinuxのインストールです。
インストールが完了しましたら一度VMを停止し、__Display__ の __Type__ を __Full Graphics__　に戻しましょう。マウントしていたisoを削除し、VMをもう一度起動すればKali Linuxが動いているのが確認できると思います。

※ __Display__ の変更をする必要があるかどうかはKali Linuxのバージョンによって異なるようです。2022-2, 2022-3では変更の必要があるそうです。2022-1以前のバージョンでは変更の必要がないようです。GitHubにissueがたってました。（https://github.com/utmapp/UTM/discussions/4044）

## Hack The Boxのマシンとの接続
以降ではVM内での操作になります。
まず初めにFireFoxを開いてHTBのアカウントを作成しましょう。HTBのアカウントを作成したらHTBのチュートリアルを通して接続してみましょう。TutorialはHTBのHomeの左下にある　__Starting Point__ というコースになります。その中にある猫のマークの __Meow__ というコースが最も簡単なコースになります。

コースの最初のステップが「Connect to Starting PointVPN before starting machine」です。このステップの右側にあるボタンを押すとOpenVPNの設定ファイルをダウンロードすることができます。このファイルをダウンロードしましたらターミナルを開き、
```
$ sudo openvpn <username>.ovpn
```
を実行してください。これで接続が完了します。（<username>内には先程ダウンロードしたファイルのパスを適宜入力してください。）
以下の記事のようにエイリアスを貼っておくと便利だと思います。
参考記事（https://naya-hira.hatenablog.com/entry/2020/02/18/000717 ）

あとはコースを進めていけばあっというまにハッカーになれるといいな。

Starting Pointの各コースの解説などもしていけたらと考えています。
Dotenvのサンプルコードを動かしてみる

## はじめに

reactで環境変数を切り替える方法を毎回忘れるのでメモりました。
ここではdotenvのサンプルコードを動かして検証していきます。
## サンプルアプリの起動

- Git
- node.js

```
git clone https://github.com/dotenv-org/examples.git
cd examploe/dotenv-react
npm i
npm run start
```

上手く起動ができました！
.envに書かれている”CHANGE_ME”が表示されてることが確認できます！

![スクリーンショット 2022-10-14 23.10.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235110/70ca349b-35f1-35f3-b480-250992f0c6a3.png)

## 環境ごとに切り替える

ここで他の環境変数へ切り替えたいので、開発用、ステージング用、本番用のenvファイルを作成します。

![スクリーンショット 2022-10-14 23.58.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235110/0c283fc8-b091-ae3d-8e5c-7538e655fea4.png)

ただReact公式ドキュメントを見ると、.env.development（開発環境）と.env.production（本番環境）は`npm run start`, `npm run build`で切り分けられていると書かれていて、これだと開発環境をデプロイしたり、ステージング環境を追加したりといったことができません。

> Files on the left have more priority than files on the right:
> npm start: `.env.development.local`, `.env.local`, `.env.development`, `.env`
> npm run build: `.env.production.local`, `.env.local`, `.env.production`, `.env`
> npm test: `.env.test.local`, `.env.test`, `.env` (note .env.local is missing)
> [What other .env files can be used?](https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used)

[dotenv-cli](https://www.npmjs.com/package/dotenv-cli)を入れて、コマンドで読み込む`.env`ファイルの読み込みを切り替えられるようにしましょう。

```
npm install --save-dev dotenv-cli
```

## スクリプトの実装
`package.json`を以下のように書き換えると、`npm run build-development`で`.env.development`を、`npm run build-production`で`.env.production`を参照するようになります。

```
  "scripts": {
    "start": "react-scripts start",
    "build-development": "dotenv -e .env.development react-scripts build",
    "build-production": "dotenv -e .env.production react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
```

## 参考サイト
https://www.npmjs.com/package/dotenv

https://www.npmjs.com/package/dotenv-cli

https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used

https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project
ORBSLAMを読んだ時のメモ書き

### Introduction  

- リアルタイムのSLAMアルゴリズムを実現するには
    - 選択したキーフレーム間の特徴点（マップ点）の観測情報の一致させる
    -  キーフレームの数が増えて複雑性が増していくにつれて、不必要な冗長性を回避すべき
    -  十分な視差があり、ループクローズに十分な対応関係を持った広いキーフレーム群
    -  非線形最適化のためのキーフレームの位置姿勢と特徴点の位置の初期推定
    -  スケーラビリティを実現するためにローカルマップで最適化を行う
    -  リアルタイムでループをクローズするための素早い全体最適化（ポーズグラフ最適化など）
  
- contribution
    - 視点変化、照度変化に対して不変のORB特徴量を用いており、GPUなしでのリアルタイム処理が可能
    - ローカルのcovisibleマップを使うことにより、全体のマップサイズとは独立してトラッキングとマッピングをすることができる
    - Essential Graphを基に行うポーズグラフ最適化に基づいたリアルタイムなループクローズ処理
    - 視点変化、照度変化に頑健なカメラリローカライズ、トラッキングに失敗したときやマップを再利用する際に復帰することができる
    - 自動の初期化処理
    - 冗長なキーフレーム削除によるトラッキングのロバスト性向上、lifelongな操作を可能にする

### Related Work

- ORB特徴量を使って、DBoW2により画像間のマッチングをする
- Bag of Wordsを利用
- トラッキングとマッピングと同じ特徴点でPlaceRecognitionをしている
- キーフレームは素早く追加して、後で削除するという思想

### System OVerview

- トラッキング、マッピング、ループクローズ、リローカライズに全て同じ特徴点を使っているのが強み
- ORBはSIFTとかに比べて早いし、視点変化にも強いから、場所認識にも強い。
- トラッキングはカメラの位置決めと、新しいキーフレームの追加のタイミングを決定する。
- ローカルマッピングスレッドは、冗長キーフレーム削除の役割を担っている。
- ループクローズスレッドでは、新しいキーフレームを追加するたびに類似キーフレームを探し、見つかったら、ループを閉じ、同じ点同士はマージする
- ループクローズのポーズグラフ最適化をessential graphを使ってやっているのが新しい
- マップ点の持つ情報
  - 三次元座標
  - 各観測キーフレームの観測方向の平均の単位ベクトル（キーフレームの光軸中心とマップ点を結ぶベクトルの平均）
  - 観測されている他のすべてのdescriptorとのハミング距離が最小となるORB descriptor
  - 点が観測される距離の最大値と最小値
- キーフレームが持つ情報
  - カメラの位置姿勢
  - カメラ内部パラメータ
  - フレーム内で検出されたORB特徴点（マップ点であるかどうかに関わらず）歪みモデルが与えられてたら、歪み補正後の座標を格納

- covisibility Graph
  - 15点以上特徴点を共有してるとキーフレーム間にエッジを張る
  - 共有特徴点数がエッジの重みになる
- essential graph
  - covisibility graphはとても密なので、エッジを減らしたグラフ。ただ、正確な結果を得るための強いネットワークは保持している。全てのノード（キーフレーム）を含む
  - spanning treeで構成され、高い共有特徴点数（最小100点）。

- map initialization
  - 平面のシーンなら、ホモグラフィーで推定、そうでなければ、基礎行列で推定
  - どっちのモデルで推定するかは、インライアの割合を見て決定。
  - 十分な示唆が得られるまで初期化しない。これにより初期化の安定性を増している。

- track local map
  - マップ点を現在のフレームに追加
    - マップ点を現在フレームに投影する（画像外なら無視する）
    - マップ点の持つ視線ベクトルと現在フレームのマップ点を見る視線ベクトルの角度が60度以内
    - カメラからの距離が閾値の範囲内
    - 投影した先の近くでORBのdescriptorが1番マッチするところ
- キーフレームの追加条件
  - 最後のリローカライズから20フレーム以上経過
  - ローカルマッピングがアイドル状態、最後のキーフレーム追加から20フレーム以上経過
  - 現在フレームが50点以上トラッキングしている
  - 参照キーフレームよりもトラッキングしている特徴点数が90%以下
  -
Amazon Linux に PostgreSQL v13 を install

リポジトリ取得
```
sudo rpm -ivh --nodeps https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
```
置換
```
sudo sed --in-place -e "s/\$releasever/7/g" /etc/yum.repos.d/pgdg-redhat-all.repo
```
インストール
```
sudo yum install -y postgresql13
```
確認
```
psql --version
```
draw.ioで作成した画面レイアウトから仕様書を自動で生成するツールを作ってみた

## このツールを作った背景
[draw.ioのライブラリを作った](https://qiita.com/capybara-alt/items/033596584f26cd96d94c)ので、実践編として投稿することにした。
前職がSIerでお客様に見せる画面イメージをdraw.ioで作成し、それを画面レイアウト定義(画面仕様書)に貼り付けて、番号や画面部品の詳細な説明などをつけていた。ただ、お客様とのミーティングで要望などを取り入れるたびにdraw.ioを修正→画面仕様書に貼り付け→画面部品の説明などの更新をしなければならなかった。この作業を少しでも楽にしたいという思いで中途半端な状態ではあるが、このツールを作成した。
## draw.ioを使う理由
draw.ioをプロジェクトで使うことになったのがきっかけで、部品にデータ（XMLのAttribute）を設定できたり、xmlファイルで管理されていることから、拡張ツールが作りやすそうと考えた。
## 実現方法
1. draw.ioで画面レイアウトを作成
2. （付番）対象の画面部品に対し、データを付与
![sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316006/7dc5ebb7-b195-17a4-794e-a0d1df1825b1.png)
![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316006/58b2e829-1fec-6dc2-5475-79d6426bb5ce.png)
3. 保存したdraw.ioファイルをpythonスクリプトで読み込み&ライブラリを用いてpythonで操作可能な状態にする
4. draw.ioのライブラリから2.で付与したデータを取得&付番
5. 付番用のdraw.ioの部品をダイアグラムに挿入
6. draw.ioで操作できる状態に戻し、ファイルを出力
7. draw.ioコマンドを用いて、6.で保存したpngファイルを出力
8. 画面仕様書のexcelテンプレートに画像を挿入
9. draw.ioで埋め込んでおいた画面部品のデータをexcelファイルに書き込む
10. excelファイルを出力
## 実行サンプル
今回は例として、IPAの画面レイアウトのサンプルを使用してみた。
Before:
![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316006/afb83123-ebf8-a25d-28de-225e6c268b4f.jpeg)
After:
![スクリーンショット 2022-10-14 23.41.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316006/002ab3e3-eb12-44fa-5df2-d4800e4d9228.png)
## 終わりに
今回作成したツールのリポジトリは[こちら](https://github.com/capybara-alt/uispecgen)。今後はバージョン管理など、このツールで全て完結できるように改善していきたい。
目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o_1o0] データファイル作成

目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o_1o0] データファイル作成

# 連載の目次

📖 [目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞](https://qiita.com/muzudho1/items/744f6051954525878b84)  

# Step [O22o_1o0] データファイル作成 - data/board2.txt ファイル

あとで使うファイルを先に作成する  

👇 以下のファイルを新規作成してほしい  

```plaintext
  	📂 kifuwarabe-uec14
	├── 📂 data
 	│	├── 📄 board1.txt
👉 	│	└── 📄 board2.txt
	├── 📂 kernel
  	│	├── 📄 board_area.go
  	│	├── 📄 board_coord.go
 	│	├── 📄 board_set.go
  	│	├── 📄 o12o__11o1o0_board.go
	│	├── 📄 go.mod
	│	├── 📄 go.sum
 	│	├── 📄 kernel.go
 	│	├── 📄 logger.go
 	│	└── 📄 stone.go
    ├── 📄 .gitignore
 	├── 📄 engine_config.go
  	├── 📄 engine.toml
    ├── 📄 go.mod
  	├── 📄 go.work
 	└── 📄 main.go
```

```plaintext
    ABCDEFGHJKLMNOPQRST
   +++++++++++++++++++++
 1 +...................+
 2 +...x...............+
 3 +..xox..............+
 4 +.xo.ox.............+
 5 +..xox..............+
 6 +...x...............+
 7 +...................+
 8 +...................+
 9 +...................+
10 +...................+
11 +...................+
12 +...................+
13 +...................+
14 +...................+
15 +...................+
16 +...................+
17 +...................+
18 +...................+
19 +...................+
   +++++++++++++++++++++
```

# Step [O22o_2o0] データファイル作成 - data/board3.txt ファイル

あとで使うファイルを先に作成する  

👇 以下のファイルを新規作成してほしい  

```plaintext
  	📂 kifuwarabe-uec14
	├── 📂 data
 	│	├── 📄 board1.txt
 	│	├── 📄 board2.txt
👉 	│	└── 📄 board3.txt
	├── 📂 kernel
  	│	├── 📄 board_area.go
  	│	├── 📄 board_coord.go
 	│	├── 📄 board_set.go
  	│	├── 📄 o12o__11o1o0_board.go
	│	├── 📄 go.mod
	│	├── 📄 go.sum
 	│	├── 📄 kernel.go
 	│	├── 📄 logger.go
 	│	└── 📄 stone.go
    ├── 📄 .gitignore
 	├── 📄 engine_config.go
  	├── 📄 engine.toml
    ├── 📄 go.mod
  	├── 📄 go.work
 	└── 📄 main.go
```

```plaintext
    ABCDEFGHJKLMNOPQRST
   +++++++++++++++++++++
 1 +...................+
 2 +..xo...............+
 3 +.xo.o..............+
 4 +..xo...............+
 5 +...................+
 6 +...................+
 7 +...................+
 8 +...................+
 9 +...................+
10 +...................+
11 +...................+
12 +...................+
13 +...................+
14 +...................+
15 +...................+
16 +...................+
17 +...................+
18 +...................+
19 +...................+
   +++++++++++++++++++++
```
lucene の CJKAnalyzer で Trigram を可能にする。

https://qiita.com/YmBIgo/items/7dafa6a31cc6fe9a0f8c

の続きのような記事です。
読んでいなかったら、読んでから来るといいと思います。

成果物

https://github.com/YmBIgo/CJKAnalyzer_mini_patch/blob/master/CJKTrigramFilter.java

## Lucene の CJK、Bigram しか対応しておらず...
Lucene の Tokenizer には、CJK という 日本語韓国語中国語 を n-gram にしてくれるものがあるんですが、それが Bigram 対応のみで Trigram 対応してくれていません。なので、ここでは、Trigram 対応の Java ファイルを作ろうと思います。

## Trigram にするためのメソッド

メソッドは、`flushTrigram` と `hasBufferedTrigram` があります。`flushTrigram` では attributeSource に登録された `CharTermAttribute` などに Trigram の内容を追加し、

```java:CJKTrigramFilter.java
  /**
    Flushes a trigram token 
  */ 
  private void flushTrigram() {
    clearAttributes();
    char[] termBuffer = termAtt.resizeBuffer(6);
    int len1 = Character.toChars(buffer[index], termBuffer, 0);
    int len2 = len1 + Character.toChars(buffer[index + 1], termBuffer, len1);
    int len3 = len2 + Character.toChars(buffer[index + 2], termBuffer, len2);
    termAtt.setLength(len3);
    offsetAtt.setOffset(startOffset[index], endOffset[index+2]);
    typeAtt.setType(TRIPLE_TYPE);
    if (outputUnigrams) {
      posIncAtt.setPositionIncrement(0);
      posLengthAtt.setPositionLength(2);
    }
    index++;
  }
```

`hasBufferedTrigram` では Trigram で分けるかどうかの判別をしています。

```java:CJKTrigramFilter.java
  private boolean hasBufferedTrigram() {
    return bufferLen - index > 2;
  }
```

一応、これで、CJKAnalyzer の CJKBigramFilter の部分を CJKTrigramFilter に変えれば、Trigram で判定が行われるようになると思います。
[CKA, CKAD, CKS共通] PSI Bridge試験環境の現状

# Motivation
CKSに受かりまして、合格記でも書こうと思いましたが、勉強方法に関する記事は「CKS 勉強法」などでググれば沢山出てきます。そこで、2022年6月25日から試験環境がWebUIからPSI　Bridgeに移行されましたので、こちらに関して試験を受けた際の使用感と注意点を記載します。

# 試験開始直後のチェック
通常kubectlの補完や、 vimrcの設定は既に設定されてあるはずです。
[Important Instructions: CKS](https://docs.linuxfoundation.org/tc-docs/certification/important-instructions-cks)
> kubectl with kalias and Bash autocompletion

[CKS CKA CKAD changed Terminal to Remote Desktop](https://itnext.io/cks-cka-ckad-changed-terminal-to-remote-desktop-157a26c1d5e)
> Make sure to set these in your .vimrc or otherwise indents will be very messy during pasting (the exams have these config settings now also by default, but can’t hurt to be able to type them down):

しかし、私の場合、kubectlの補完がされていませんでした。
そこで、以下のページはすぐに開けるようにしておいた方がいいです。
[kubectlチートシート: Kubectlコマンドの補完](https://kubernetes.io/ja/docs/reference/kubectl/cheatsheet/#kubectl%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E8%A3%9C%E5%AE%8C)

また、念のため.vimrcの設定は暗記しておきましょう。
```
set smarttab
set expandtab
set shiftwidth=2
set tabstop=2
```

# firefoxについて
私は受験時、13.3inchディスプレイのMacBook Airを使用しました。
ディスプレイサイズが小さいPCで受験を検討している方もいるかと思いますが、試験環境のブラウザであるfirefox利用時の以下の経験からまったくおすすめしません。
1. 全画面表示できない
ディスプレイサイズが小さい場合、以下のドキュメントの赤枠部分が途中で切れてしまい、見えません。
項目ごとのジャンプが出来ず非常に不便でした。
![k8s_doc_screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117488/ca8a088b-0208-56bf-091c-68b46cf3dffe.png)

1. Ctrl + Fの検索窓が見えない
試験で一番困ったのはこれです。
大きなページである場合に、ページ内で検索が出来ず、目的箇所を探すのに非常に時間を要しました。

# ラグについて
以下のページに記載の通り、受験時のAWSリージョンが遠いため高レイテンシ問題が発生していた、現在は解消していると記載があります。
[Update on Certification Exam Proctoring Migration
](https://training.linuxfoundation.org/blog/update-on-certification-exam-proctoring-migration/?utm_source=lftraining&utm_medium=twitter&utm_campaign=blog)
| ISSUE | RESOLUTION |
| ----- | -----|
| Lag   | Exams provisioned on 06/25 and 06/26 did NOT provision from the AWS region closest to the candidate’s geographical location, which caused high latency issues. This issue is now resolved.Some lag is expected in a remote-desktop/VNC connection environment, such as when moving windows or scrolling through pages like CNCF documentation.We will continue to monitor latency reports and will expand into new AWS regions as needed.|

しかし、以下の記事のように作業に大きなストレスを感じるくらいにはラグが感じられました。
受験時のドキュメントスクロール時にフリーズも発生しました。

[CKS CKA CKAD changed Terminal to Remote Desktop
](https://itnext.io/cks-cka-ckad-changed-terminal-to-remote-desktop-157a26c1d5e)
>Speed
Coming from your local desktop you’ll notice lagging in the remote desktop. Like when moving windows or scrolling through the K8s Docs. It’s what you would expect from a VNC connection and you should adopt to it after a few minutes. But many users reported huge lagging that made working not possible. According to the Linux Foundation this should be fixed.

近い将来、日本に近いリージョンで受験できる日が来れば解消するのだろうか？
それまでは早いNW環境で受験をお勧めします。

# まとめ
- kubectl補完とvim設定は忘れずに
- 可能な限り大きなディスプレイを用意 
- 早いNW環境を用意
目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o1o0] 空点以外のところ（石または枠の上）に石を置くことの禁止

目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o1o0] 空点以外のところ（石または枠の上）に石を置くことの禁止

# 連載の目次

📖 [目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞](https://qiita.com/muzudho1/items/744f6051954525878b84)  

# Step [O22o0] 囲碁の石を打つルールの実装

# Step [O22o1o0] 空点以外のところ（石または枠の上）に石を置くことの禁止 - IsMasonry関数作成

とりあえず、 `石または枠の上に石を置く行為` に `Masonry` （メイスンリー）という名前を付ける。  
従って この主のエラーは `Masonry error` と呼ぶことにする。  
そのようなエラーであるかどうか判定する関数の名前は `IsMasonry` と呼ぶことにする  

## Step [O22o1o1o0] ファイル作成 - masonry.go

👇 以下の既存ファイルを編集してほしい  

```plaintext
  	📂 kifuwarabe-uec14
	├── 📂 kernel
	│	├── 📂 play_rule
  	│	├── 📄 board_area.go
  	│	├── 📄 board_coord.go
  	│	├── 📄 o12o__11o1o0_board.go
	│	├── 📄 go.mod
	│	├── 📄 go.sum
 	│	├── 📄 kernel.go
 	│	├── 📄 logger.go
👉 	│	├── 📄 masonry.go
 	│	├── 📄 play.go
 	│	├── 📄 o12o__10o1o0_point.go
 	│	└── 📄 stone.go
    ├── 📄 .gitignore
 	├── 📄 engine_config.go
  	├── 📄 engine.toml
    ├── 📄 go.mod
  	├── 📄 go.work
	└── 📄 main.go
```

```go
// BOF [O22o1o1o0]

package kernel

// IsMasonry - 石の上に石を置こうとしたか？
func (b *Board) IsMasonry(point Point) bool {
	// 空点以外に石を置こうとしたら、石の上に石を置いた扱いにする
	return !b.IsSpaceAt(point)
}

// EOF [O22o1o1o0]
```

## Step [O22o1o2o0] 呼出し

👇 以下の既存ファイルを編集してほしい  

```plaintext
  	📂 kifuwarabe-uec14
	├── 📂 kernel
	│	├── 📂 play_rule
  	│	├── 📄 board_area.go
  	│	├── 📄 board_coord.go
  	│	├── 📄 o12o__11o1o0_board.go
	│	├── 📄 go.mod
	│	├── 📄 go.sum
 	│	├── 📄 kernel.go
 	│	├── 📄 logger.go
 	│	├── 📄 masonry.go
👉 	│	├── 📄 play.go
 	│	├── 📄 o12o__10o1o0_point.go
 	│	└── 📄 stone.go
    ├── 📄 .gitignore
 	├── 📄 engine_config.go
  	├── 📄 engine.toml
    ├── 📄 go.mod
  	├── 📄 go.work
	└── 📄 main.go
```

👇 がんばって以下の箇所に挿入してほしい  

```go
// func (k *Kernel) DoPlay(command string, logg *Logger) {
	// ...略...
	// var point = k.Board.GetPointFromGtpMove(tokens[2])

	// * 以下を追加
	// [O22o1o2o0]
	var onMasonry = func() bool {
		logg.C.Infof("? masonry my_stone:%s point:%s\n", stone, point)
		logg.J.Infow("error", "my_stone", stone, "point", point)
		return false
	}

	// var isOk = k.Play(stone, point, logg,
		// * 以下を追加
		// [O22o1o2o0] ,onMasonry
		onMasonry//)
	// ...略...
// }

// func (k *Kernel) Play(stone Stone, point Point, logg *Logger,
	// * 以下を追加
	// [O22o1o2o0] onMasonry
	onMasonry func() bool//) bool {

	// * 以下を追加
	// [O22o1o2o0]
	if k.Board.IsMasonry(point) {
		return onMasonry()
	}

//	k.Board.cells[point] = stone
//	return true
// }
```
目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o3o0] 相手の眼に石を置くことの禁止

目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞ Step [O22o3o0] 相手の眼に石を置くことの禁止

# 連載の目次

📖 [目指せ！第１４回ＵＥＣ杯コンピューター囲碁大会＜本編＞](https://qiita.com/muzudho1/items/744f6051954525878b84)  

# Step [O22o3o0] 相手の眼に石を置くことの禁止 - OpponentEye

囲碁のルールでは、相手の眼へは石を置けない。これを判定する  

とりあえず、このルールへ直訳で短い名前を付ける。 仮に `OpponentEye` とでも呼ぶことにする  

このルールは、あとで出てくる `Captured` のルールよりは優先度が低いとする  

## Step [O22o3o1o0] ファイル編集 - kernel.go

👇 以下の既存ファイルを編集してほしい  

```plaintext
  	📂 kifuwarabe-uec14
	├── 📂 data
 	│	└── 📄 board1.txt
	├── 📂 kernel
	│	├── 📂 play_rule
  	│	├── 📄 board_area.go
  	│	├── 📄 board_coord.go
  	│	├── 📄 o12o__11o1o0_board.go
 	│	├── 📄 check_board.go
 	│	├── 📄 color.go
	│	├── 📄 go.mod
	│	├── 📄 go.sum
 	│	├── 📄 kernel.go
 	│	├── 📄 liberty.go
 	│	├── 📄 logger.go
 	│	├── 📄 masonry.go
👉 	│	├── 📄 play.go
 	│	├── 📄 o12o__10o1o0_point.go
 	│	├── 📄 ren.go
 	│	└── 📄 stone.go
    ├── 📄 .gitignore
 	├── 📄 engine_config.go
  	├── 📄 engine.toml
    ├── 📄 go.mod
  	├── 📄 go.work
	└── 📄 main.go
```

👇 がんばって挿入してほしい  

```go
// func (k *Kernel) DoPlay(command string, logg *Logger) {

	// ...略...
	// [O22o3o1o0] 相手の眼に石を置こうとした
	var onOpponentEye = func() bool {
		logg.C.Infof("? opponent_eye my_stone:%s point:%s\n", stone, k.Board.coordinate.GetGtpMoveFromPoint(point))
		logg.J.Infow("error opponent_eye", "my_stone", stone, "point", k.Board.coordinate.GetGtpMoveFromPoint(point))
		return false
	}

//	var isOk = k.Play(stone, point, logg,
//		// [O22o1o2o0] ,onMasonry
//		onMasonry,
		// [O22o3o1o0] ,onOpponentEye
		onOpponentEye//)
//
//	if isOk {
//		logg.C.Info("=\n")
//		logg.J.Infow("ok")
//	}
// }

// func (k *Kernel) Play(stoneA Stone, pointB Point, logg *Logger,
	// // [O22o1o2o0] onMasonry
	// onMasonry func() bool,
	// [O22o3o1o0] onOpponentEye
	onOpponentEye func() bool//) bool {

	// ...略...
	// // [O22o1o2o0]
	// if k.Board.IsMasonry(point) {
	//	return onMasonry()
	// }

	// [O22o3o1o0]
	var renC, isFound = k.GetLiberty(pointB)
	if isFound && renC.GetArea() == 1 { // 石Aを置いた交点を含む連Cについて、連Cの面積が1である（眼）
		if stoneA.GetColor() == renC.adjacentColor.GetOpponent() {
			// かつ、連Cに隣接する連の色が、石Aのちょうど反対側の色であったなら、
			// 相手の眼に石を置こうとしたとみなす
			return onOpponentEye()
		}
	}

	// ...略...
	// k.Board.cells[point] = stone
	// return true
```

## Step [O22o3o2o0] 動作確認

👇 以下のコマンドをコピーして、ターミナルに貼り付けてほしい

Input:  

```shell
go run .
```

これで、思考エンジン内の入力待機ループに入った  

👇 以下のコマンドをコピーして、ターミナルに貼り付けてほしい  

Input:  

```shell
board_set file data/board1.txt
play white C3
```

Output > Console:  

```plaintext
[2022-09-17 00:41:29]   # play white C3
[2022-09-17 00:41:29]   ? opponent_eye my_stone:o point:C3
```