Quantcast
Channel: ほねっとのぶろぐ
Viewing all 129 articles
Browse latest View live

タイムズカープラス株主優待の使い方

$
0
0

f:id:aftercider:20170422095816p:plain

カーシェアリングのタイムズカープラスでは、株主優待券で60分×2回が安くなるプラスeチケットをもらえます。

ただ使い方が普通の検索&タイムズカープラスFAQでは出てこないため、ちょっと難しい。

なので、備忘録も兼ねてメモ。

タイムズカープラス ギフトコード利用方法

1. ギフトコードを手元に準備。6桁の英数字です。

2. タイムズカープラスにログイン。
https://plus.timescar.jp/

3. ギフトコード入力画面を開く。
https://plus.timescar.jp/view/member/gift/input.jsp

このページの入り口がマイページとかには表示されてないのが難関。

4. 表示された画面にギフトコードを入力して決定。eチケットが配布されます。

5. 車の予約時に、持っているeチケットを利用。

使い方:http://plus.timescar.jp/faq/use/ticket/05.html

 

というかんじです。

 

 

最近調子がいいのか、近くのカーシェアステーションも増え、車も新車が増えてきています。

どんどん便利&快適になってくれていてありがたいです。


炊飯器を買いました

$
0
0

GW、電器屋さんに行ってとうとう買っちゃいました、炊飯器!

一人暮らし時代からずっと使ってきたんですが、もう結構経ってきたし、キッチンの食器棚を新調したので、購入を決意しました。

買ったのはこちら!

 

 

https://www.instagram.com/p/BTiy0WBDbvu/

10万の炊飯器頑張れー

なかなか良いのに手を出してしまった。

内釜に鉄球が入ってるんだぜ…!

炊き上がりはとってもきれい!お米がみんなキラキラしてて、しっかりとした存在感。ご飯が進むのでおかわりしないように気を付けないと…。

炊き込みご飯もしっかり美味しくなってたのでよかったです。

そして今週末には同じくGW中に購入したドラム式洗濯機が届きます。こちらもたのしみ!

パナソニックの洗濯機NA-VX8700L買った!

$
0
0

GWのお買い物シリーズ第二段、ドラム式洗濯機を買いました!

買ったのはこの洗濯機

これまでは一人暮らし時の縦型洗濯機を使い続けてきましたが、
お仕事が忙しくなると休みの日しか洗濯ができず、洗濯物がたまりがち。

GWの連休を機に、洗濯から乾燥までやってくれるドラム式洗濯機を購入!

購入したのはパナソニックのNA-VX8700L。

11kgの洗濯容量で、洗濯後の感想はヒートポンプ式。
温水洗浄に対応したミドルレンジの洗濯機です。

ヨドバシカメラへ!

洗濯機は長く使うものなので、ネットショップではなく実店舗で購入して保証をつけたいところ。

購入のためヨドバシカメラに行くと、GW期間中のスペシャルバーゲンということで、1万円の値引きがデフォルト付与!

ありがたい(涙)

店員さんの解説

他の商品とじっくり見比べていると、店員さんが説明してくださりました。

もう一つ目をつけていた東芝の洗濯機と比較して、どうしようかなーと話しつつ、価格交渉。

その結果、さらに約1万円お値引きいただけることに!

ありがたやーありがたやー!

結果、店頭表示価格より2万円のお値引きいただいた上で購入できました。

配送はまた後日なので、届くのが楽しみです。

セリーヌさん引けるか…!?

$
0
0

スターオーシャンアナムネシス、新キャラのセリーヌさん引きたいいいいいいいいいい!


f:id:aftercider:20170515223042j:image

引くぞ~!


f:id:aftercider:20170515223058j:image

うわぁぁぁぁぁぁぁぁぁぁぁぁぁぁ!(ダメだー)

さらにもう一回!

どきどき…!


f:id:aftercider:20170515223205j:image

きたあああああああ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


f:id:aftercider:20170515223215j:image

うひょー!!!!(低解像度にしちゃったあああああ)


f:id:aftercider:20170515223241j:image

麗しい!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


f:id:aftercider:20170515223256j:image

やりました、やりましたぞ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

【スターオーシャン3DC】 デーモンロード戦動画がすごい

$
0
0

すげー!

なにがすごいかって、マニュアル操作で二人を操作しつつ、一度でも攻撃が当たったら即死の敵二人を相手してるところがすげー!

スターオーシャン3DC、PS4でリマスター版が出てるので、PS2版をやっていた私もよくやってるんですが、やっぱりこのスターオーシャンは面白いですね。

アナムネシスもずっと継続してますが、花嫁ネルさんがでない・・・あとブルネリ殿もせっかくだからほしいんだけど、石が足りない・・・

きっと描けば出るってやつだと思われる。

【スターオーシャンアナムネシス】花嫁ネルさんほか、花嫁シリーズ終了のおしらせ

$
0
0


f:id:aftercider:20170706111239j:image

これがラストチャンス!!!!!!!!!!!!!!!!!!!!!!!!

ガチャ引けるようにヴィクトル10凸して試練の塔最上階までクリアしたんだ!!!

f:id:aftercider:20170706110824j:image

いくぞ!!!


f:id:aftercider:20170706110945j:image

ズコーッ!

全部石ー!

(ショックでスクショとりわすれました)

復刻ガチャお願いします…

バフェットの本読んだし次は村上ファンドの本読む

$
0
0

読んだ

 

バフェット 伝説の投資教室--パートナーへの手紙が教える賢者の哲学

バフェット 伝説の投資教室--パートナーへの手紙が教える賢者の哲学

 

 なので、次はこれ読む

 

生涯投資家

生涯投資家

 

 これも読んだ。素晴らしかった。熱く語りたい。

 

ソードアート・オンライン20 ムーン・クレイドル (電撃文庫)

ソードアート・オンライン20 ムーン・クレイドル (電撃文庫)

 

 

村上ファンドの村上さんが書いた生涯投資家読んだので次はこれ読もう

$
0
0

f:id:aftercider:20171103231747j:image

読み終わったよぉ~!

ハードカバーででかいから、なかなか鞄に入れてなくて読むチャンスが少なかったので、まとまった時間にがっつり読んだ。

信念ある行動は人を惹き付けますねー。

つぎはyasuさんにおすすめいただいた本読もうかなと思ったけど、Readerストアになかったから別のになるかも。

yasu/HIRATA Yasuyuki on Twitter: "@aftercider 村上さんの本、ちょっと気になってる。(もう読んでるかもしれませんが、オススメはシーゲル教授の「株式投資」「株式投資の未来」です。)"

 

株式投資 第4版

株式投資 第4版

  • 作者:ジェレミー・シーゲル,藤野隆太,林康史,石川由美子,鍋井里依,宮川修子
  • 出版社/メーカー:日経BP社
  • 発売日: 2009/07/23
  • メディア:単行本
  • クリック: 14回
  • この商品を含むブログ (3件) を見る
 

 それはそうと、ゆかりんかわいい。

今日のFCイベント最高最高最高!

 

【Amazon.co.jp限定】Princess Limited (L判ブロマイド付)

【Amazon.co.jp限定】Princess Limited (L判ブロマイド付)

 

 


堀江貴文(ホリエモン)の『稼ぐが勝ち』を読みました

$
0
0

f:id:aftercider:20171106000716j:image

ホリエモンこと、堀江貴文さんの本はたまに読むんですが、今回は比較的古い本に手を出してみた。

10年以上前の、まだライブドアがバリバリだった頃の本なのに、書いてあることは未来日記のようにズバズバと当たっていてビックリ。

インターネットに置き換わるテレビや、ネットワークゲームが全盛のソシャゲなどなど。

本のポイントはそれとは別のとこで、またそれもなかなかできそうでみんなできてないところ。

意識してやってみよ。

 

稼ぐが勝ち?ゼロから100億、ボクのやり方? (光文社知恵の森文庫)

稼ぐが勝ち?ゼロから100億、ボクのやり方? (光文社知恵の森文庫)

 

 

Flutterでカメラアプリを作る〜プレビュー表示編〜

$
0
0

f:id:aftercider:20181114175259p:plain

Flutterという、ワンソースでiOSアプリもAndroidアプリもビルドできるGoogle製フレームワークがありまして、そのFlutterを使ってカメラのプレビュー・撮影・保存までのやり方をまとめました。

この記事ではプレビューまでをまとめ、次の記事では撮影・保存をまとめます。

前提

  • Flutter 0.11.3
  • MacBookPro(Mac Mojave)環境
  • Flutterは初めてさわる

インストール

公式ドキュメントにステップバイステップで書いてあるので、これを見ながら導入します。

https://flutter.io/docs/get-started/install

AndroidStudid, XCode, VisualStudioCodeは導入していたので、プラグインだけ設定して完了です。

pubspec.yamlにcameraモジュールを追加

Androidでいうところのapp/buld.gradle、Nodejsでいうところのpackage.jsonにあたるのが、pubspec.yamlです。

YAMLはインデントを使って構造を表現するようになっているので、ぱっと見で大体わかります。

今回はcameraモジュールを追加しましょう。

dependenciesの中にcamera: ^0.2.4を追加します。

name: hello_world_project
description: HelloWorldProject

version: 1.0.0+1

environment:
  sdk: ">=2.0.0-dev.68.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  english_words: ^3.1.0
  camera: ^0.2.4

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

AndroidStudioの右上からPackages getを実行して、モジュール群を取得します。AndroidでいうところのSyncですね。

f:id:aftercider:20181114174256p:plain

(Androidのみ)minSDKVersionを21以上にする

cameraモジュールのスペック要件がminSdkVersion:21以上なので、android/app/build.gradleを開き、android ->defaultConfig ->minSdkVersionを21にします。(筆者環境下での初期値は16になってました。)

f:id:aftercider:20181114174255p:plain

カメラのプレビューを表示する

こんな感じで、main.dartを作ります。

import 'dart:async';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

// カメラ情報のリスト
List<CameraDescription> cameras;

// ここから始まる
Future<Null> main() async {
  cameras = await availableCameras();
  runApp(new CameraApp());
}

// カメラを表示するStatefulWidget
class CameraApp extends StatefulWidget {
  @override
  _CameraAppState createState() => new _CameraAppState();
}

class _CameraAppState extends State<CameraApp> {
  CameraController controller;

  @override
  void initState() {
    super.initState();
    // 背面カメラを高解像度で初期化して表示スタート
    controller = new CameraController(cameras[0], ResolutionPreset.high);
    controller.initialize().then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
    });
  }

  @override
  void dispose() {
    // 使い終わったらカメラを解放
    controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!controller.value.isInitialized) {
      return new Container();
    }
    return new AspectRatio(
        aspectRatio: controller.value.aspectRatio,
        child: new CameraPreview(controller));
  }
}

実機で実行

端末をつなぎ、画面上の緑色の実行ボタンをクリックして実行します。

f:id:aftercider:20181114174249p:plain

実行すると、カメラなどのパーミッション要求もきちんとやってくれて、アプリが立ち上がります。

IDE側のLogcatもちゃんと見れるようになっていて、Android開発者にも優しいです。

ちなみにプレビューはこんな感じです。(縦長になってしまうのはカメラの縦横比とライブビューの縦横比が違うため)

f:id:aftercider:20181114174225p:plain

次の記事

次の記事では撮影・保存を行います。

Flutterでカメラアプリを作る〜撮影・保存編〜

$
0
0

f:id:aftercider:20181116165525p:plain Flutterという、ワンソースでiOSアプリもAndroidアプリもビルドできるGoogle製フレームワークがありまして、そのFlutterを使ってカメラのプレビュー・撮影・保存までのやり方をまとめました。

以下の記事では、環境設定とプレビューまでをまとめました。

blog.aftercider.com

前提

  • Flutter 0.11.3
  • MacBookPro(Mac Mojave)環境
  • Flutterは初めてさわる

撮影コード追加

package導入

まず、package.yamlにファイルパスをサポートしてくれるpath_providerを導入して、$ flutter packages getします。

name: hello_world_project
description: HelloWorldProject

version: 1.0.0+1

environment:
  sdk: ">=2.0.0-dev.68.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  english_words: ^3.1.0
  camera: ^0.2.4
  path_provider: ^0.4.1

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

プログラムを書く

プレビュー編で行なった通りcameraモジュールの動作要件に合わせ、AndroidのminSdkVersionは21にしておきましょう。

cameraモジュールのExampleにサンプルコードが掲載されているんですが、 静止画撮影・動画撮影の両方のコードが書いてあり、ボリュームも大きいので初心者にはちょっと難易度が高いです。

また、FlutterのUIに関するコードも結構な量あるため、どこがカメラ制御用のコードなのかわからないっていう難しさもあります。

ということで、今回はFlutterでカメラを使えるようになることを目的として、

  • 例外処理・UI装飾をできるだけなくして見通しをよくする
  • 録画関連・サムネイル表示のコードを削除
  • コメントを追加

といった対応を入れて、撮影・保存のみできるようにします。

main.dartを以下のようにしていきました。

import 'dart:async'; // 非同期処理(async/await)
import 'dart:io'; // ファイルの入出力

import 'package:camera/camera.dart'; // カメラモジュール
import 'package:flutter/material.dart'; // マテリアルデザイン
import 'package:path_provider/path_provider.dart'; // ファイルパスモジュール

List<CameraDescription> cameras; // 使用できるカメラのリスト

// ここから始まる
Future<Null> main() async {
  cameras = await availableCameras();
  runApp(CameraApp());
}

// 親玉のApp
class CameraApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CameraWidget(),
    );
  }
}

// 親玉の中身
class CameraWidget extends StatefulWidget {
  @override
  _CameraWidgetState createState() {
    return _CameraWidgetState();
  }
}

// 実際はこれがやることやる
class _CameraWidgetState extends State<CameraWidget> {
  CameraController controller;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  String timestamp() =>
      DateTime.now().millisecondsSinceEpoch.toString(); // ファイル名にはタイムスタンプ入れる。
  void showInSnackBar(String message) => _scaffoldKey.currentState
      .showSnackBar(SnackBar(content: Text(message))); // SnackBarでメッセージ表示

  @override
  Widget build(BuildContext context) {
    Scaffold sc = Scaffold(
      key: _scaffoldKey,
      body: Column(
        children: <Widget>[
          Expanded(
            child: Container(
              child: Padding(
                padding: const EdgeInsets.all(1.0),
                child: Center(
                  child: _cameraPreviewWidget(), // カメラのプレビューを表示するWidget
                ),
              ),
            ),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              IconButton(
                // カメラの撮影ボタン
                icon: const Icon(Icons.camera_alt),
                onPressed: controller != null && controller.value.isInitialized
                    ? onTakePictureButtonPressed // 撮影ボタンを押された時にコールバックされる関数
                    : null,
              ),
            ],
          )
        ],
      ),
    );

    // カメラのセットアップ。セットアップが終わったらもう一回buildが走るので、
    // controllerがnullかどうかで処理実施有無を判定。
    if (controller == null) {
      setUpCamera(cameras[0]);
    }
    return sc;
  }

  /// カメラプレビューを表示するWidget
  Widget _cameraPreviewWidget() {
    if (controller == null || !controller.value.isInitialized) {
      // カメラの準備ができるまではテキストを表示
      return const Text('Tap a camera');
    } else {
      // 準備ができたらプレビュー表示
      return AspectRatio(
        aspectRatio: controller.value.aspectRatio,
        child: CameraPreview(controller),
      );
    }
  }

  // カメラを準備する
  void setUpCamera(CameraDescription cameraDescription) async {
    if (controller != null) {
      await controller.dispose();
    }
    controller = CameraController(cameraDescription, ResolutionPreset.high);

    // カメラの情報が更新されたら呼ばれるリスナー設定
    controller.addListener(() {
      if (mounted) setState(() {}); // 準備終わったらbuildし直す。
      if (controller.value.hasError) {
        showInSnackBar('Camera error ${controller.value.errorDescription}');
      }
    });

    await controller.initialize();

    if (mounted) {
      setState(() {});
    }
  }

  // 撮影ボタンが押されたら撮影して、画像を保存する
  void onTakePictureButtonPressed() {
    takePicture().then((String filePath) {
      if (mounted) {
        setState(() {});
        if (filePath != null) showInSnackBar('Picture saved to $filePath');
      }
    });
  }

  // 画像保存処理
  Future<String> takePicture() async {
    if (!controller.value.isInitialized) {
      return null;
    }
    final Directory extDir = await getApplicationDocumentsDirectory();
    final String dirPath = '${extDir.path}/Pictures/flutter_test';
    await Directory(dirPath).create(recursive: true);
    final String filePath = '$dirPath/${timestamp()}.jpg';

    if (controller.value.isTakingPicture) {
      return null;
    }

    await controller.takePicture(filePath);
    return filePath;
  }
}

実機で実行

実機で実行すると、カメラプレビューと撮影ボタンがこんな感じで表示されます。

f:id:aftercider:20181116165705p:plain

課題

撮影してみるとわかるんですが、iOSもAndroidもtakePictureのなかで保存先ファイルパスをgetApplicationDocumentsDirectoryで取得していて、アプリ領域に画像を保存しているためため、ギャラリーアプリや写真アプリで撮影画像を見ることができません。

次回はギャラリーアプリや写真アプリで見れる領域に保存する方法をまとめます。

Android/iOSクロス開発フレームワーク Flutter入門

Android/iOSクロス開発フレームワーク Flutter入門

Flutterでカメラアプリを作る〜写真保存編〜

$
0
0

f:id:aftercider:20181208230023p:plain

前置き

FlutterAdventCalender2018 #2 に参加させていただきました。

qiita.com

この記事の前と、そのさらに前の合計3記事でワンセットになります。

blog.aftercider.comblog.aftercider.com

前に投稿した2つの記事は他の方も紹介していたり、ライブラリのExampleでだいたい理解できるところではあるのですが、今回のテーマとした「写真の保存」については個人的にも苦戦したため、AdventCalendarのエントリとしてまとめてみました。

環境

  • Flutter (Channel stable, v1.0.0, on Mac OS X 10.14.1 18B75, locale ja-JP)
  • Android toolchain - develop for Android devices (Android SDK 28.0.3)
  • iOS toolchain - develop for iOS devices (Xcode 10.1)
  • Android Studio (version 3.2)

記事書いてる途中でFlutter 1.0.0が出て嬉しいかぎりです!

想定する読者

  • iOS開発/Android開発がなんとなくわかる
  • Flutterは初心者(なので、クラス分割は行わず、ワンソースで処理を追えるようにします)

やりたいこと

以前の記事で、カメラプレビューの表示・撮影、そしてアプリケーション領域への撮影画像の保存をまとめました。

今回は撮影画像の保存先を他のアプリやPCからアクセスできるようにしていきます。

Androidであればシステム外部のストレージ領域、iOSであればPhotoライブラリ領域(別名 写真領域)に保存していきます。

ファイル保存のパーミッション設定

アプリケーション領域と異なり、Android・iOSそれぞれアプリケーション管轄外の保存領域に書き込むため、Permissionの取得が必要です。

パーミッション管理モジュールは、simple_permissionモジュールを使います。

pub.dartlang.org

(ちなみに最初、permission_handlerモジュールを最初使ったのですが、iOS環境でビルドするのに難儀したため、使用するのを断念しました。)

https://pub.dartlang.org/packages/permission_handler

Androidの設定

まずは設定の簡単なAndroidの方から進めます。

しばらく色々触った感想なのですが、Androidではサクッと動くモジュールも、iOSになると何か手を入れないと動かない、みたいなことが多いような気がしています。 それもあって、まずはAndroidから作るようなプロセスを今のところやっています。

以下のように、android/app/src/main/AndroidManufest/xmlに WRITE_EXTERNAL_STORAGEのパーミッションを要求する宣言を追加します。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.aftercider.cameraapp">

    <!-- The INTERNET permission is required for development. Specifically,
         flutter needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.

iOSの設定

iOSではPhotoLibraryにアクセスするためには、Info.plistに用途について記述することが必要です。

以下のように項目を追加します。詳しくはこちらの記事を。

dev.classmethod.jp

ちなみに私の環境だと、simple_permissionを導入するとpodでエラーが発生するようになったため、ios/podfileの2行目を以下のように変更を入れる必要がありました。 Issueにも上がってるみたいです。

github.com

# Uncomment this line to define a global platform for your project
platform :ios, '10.0'
use_frameworks!

コードの追加

AndroidであればWRITE_EXTERNAL_STORAGE、iOSであればPhotoLibraryへのアクセスについて、パーミッションの確認・要求を行う処理を追加します。

  // カメラを準備する
  void setUpCamera(CameraDescription cameraDescription) async {
    if (controller != null) {
      await controller.dispose();
    }
    controller = CameraController(cameraDescription, ResolutionPreset.high);

    // カメラの情報が更新されたら呼ばれるリスナー設定
    controller.addListener(() {
      if (mounted) setState(() {}); // 準備終わったらbuildし直す。
      if (controller.value.hasError) {
        showInSnackBar('Camera error ${controller.value.errorDescription}');
      }
    });

    await controller.initialize();

    // パーミッションの確認・要求
    if (Platform.isAndroid &&
        !await SimplePermissions.checkPermission(Permission.WriteExternalStorage)) {
      SimplePermissions.requestPermission(Permission.WriteExternalStorage);
    } else if (Platform.isIOS &&
        !await SimplePermissions.checkPermission(Permission.PhotoLibrary)) {
      SimplePermissions.requestPermission(Permission.PhotoLibrary);
    }

    if (mounted) {
      setState(() {});
    }
  }

これで、アプリケーションが起動して、setUpCameraが呼ばれるタイミングでパーミッションの要求が実行されるようになります。

撮影画像の保存

次は、許可が取れた領域に撮影した画像を保存する処理を追加していきます。 Androidについては非常に簡単で、 path_providerモジュールがほとんどよしなにやってくれます。 getApplicationDocumentsDirectoryで取得していたディレクトリを、getExternalStorageDirectoryで取得するように変更すればOKです。 この際、WRITE_EXTERNAL_STORAGEのpermissionが取得できていない場合、保存に失敗するのでご注意ください。

  // Before
  final Directory extDir = await getApplicationDocumentsDirectory(); // アプリケーション領域
  final String dirPath = '${extDir.path}/Pictures/flutter_test';
  await Directory(dirPath).create(recursive: true);
  final String filePath = '$dirPath/${timestamp()}.jpg';
  // After
  final Directory extDir = await getExternalStorageDirectory(); // 外部領域
  final String dirPath = '${extDir.path}/Pictures/flutter_test';
  await Directory(dirPath).create(recursive: true);
  final String filePath = '$dirPath/${timestamp()}.jpg';

iOSでの処理

Androidで使用したgetExternalStorageDirectoryの関数は、残念ながらiOSでは使うことができません。 そこで、iOSのPhotoLibrary領域に写真を保存するために、 image_picker_saverモジュールを追加します。

pub.dartlang.org

pubspec.yamlにimage_picker_saverを以下のように追加します。

environment:
  sdk: ">=2.0.0-dev.68.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  camera: ^0.2.6
  path_provider: ^0.4.1
  simple_permissions: ^0.1.9
  image_picker_saver: ^0.1.0

image_picker_saverは、保存したいバイト配列を渡すと、PhotoLibrary領域に画像を保存してくれる機能を持っています。

ということで、iOSでは、写真撮影した画像をtemporary領域に一時画像として保存し、image_picker_saverを使って、PhotoLibrary領域にコピーするといった流れで、撮影画像を保存していきます。

  // 画像撮影・保存処理
  Future<String> takePicture() async {
    if (!controller.value.isInitialized) {
      return null;
    }

    Directory dir;
    if (Platform.isAndroid) {
      dir = await getExternalStorageDirectory(); // 外部ストレージに保存
    } else if (Platform.isIOS) {
      dir = await getTemporaryDirectory(); // 一時ディレクトリに保存
    } else {
      return null;
    }

    final String dirPath = '${dir.path}/Pictures/flutter_test';
    await Directory(dirPath).create(recursive: true);
    String filePath = '$dirPath/${timestamp()}.jpg';

    if (controller.value.isTakingPicture) {
      return null;
    }

    await controller.takePicture(filePath);

    // filePathに保存されたデータをiOSならPhotoLibrary領域にコピーする
    if (Platform.isIOS) {
      String tmpPath = filePath;
      var savedFile = File.fromUri(Uri.file(tmpPath));
      filePath = await ImagePickerSaver.saveFile(
          fileData: savedFile.readAsBytesSync());
    }

    return filePath;
  }

動作確認

Android

AndroidEmulatorで動作させるとこんな感じの動作画面です。画面下部に表示された撮影先ファイルパスもちゃんと外部領域になっています。

f:id:aftercider:20181208225318p:plain
Android動作イメージ

iOS

iOSはシミュレーターだとカメラが動かせないので、手持ちのiPad miniで動作させました。 Androidと同じく、撮影されたファイルパスがきちんとPhotoLibrary領域になっています。撮影された画像も写真アプリからちゃんと閲覧することができています。

f:id:aftercider:20181208225356p:plain
iOS動作イメージ

まとめ

コード量としては10行ちょっとの追加で、保存先を外部領域/PhotoLibrary領域に保存することができました。 Flutter 1.0.0が出たばっかりとはいえ、ライブラリ群がかなり準備されているおかげです。

コードについても、Dartがそこまで癖がある言語ではないこともあり、コード補完で大体出来上がった感じでした。 ただ、Podfileの変更や、AndroidManufest・Info.plistなど、各プラットフォームの開発経験がないと結構つまづきやすいところもあるので、その辺の知識はFlutter開発するにあたっても必須になるなと感じました。

また、今回コードをワンソースで見れるっていう形で作ったこともありOSでの分岐がどうしても発生してしまいましたが、クラス・モジュール設計をきちんとやるとロジック側ではあまりOS感の違いを意識しない作りにすることも可能だと思います。

もちろん、プロダクションレベルではモジュールとして隠蔽できるようにした作りにするのがいいかと思います。

書いたコード

作ったプロジェクトについてはGithubにおいておきました。ここには書ききれなかったコードの細かい変更等はそちらでご確認いただけると幸いです。

github.com

開発者がDockerを使うメリット

$
0
0

f:id:aftercider:20190316234633p:plain

あらまし

開発は自社でやるけれど、運用は協力会社にお任せ!といったシーンが多くあると、 Dockerをはじめとしたコンテナ技術が、自分の開発業務にどう役立つかのイメージができないことがあります(ありました)。

そこで、開発・設計や技術検討、小さめのプロダクトでどう役立つかという視点で、Dockerを紹介します。

巷のウェブサービスのお試し

巷にはたくさんのウェブサービスが転がっていて、試してみたくてもなかなか忙しくて環境構築ができず試せないことが多いです。

コンテナが使えると、パッと環境作って試して、パッと環境ごと捨てて、何事もなかったかのようにいつもの業務に戻ることができます。

コンテナは手元の環境と独立しているので、手元のMySQLやPHPのバージョンの設定にも全く影響がないので、3秒くらいですんなりお別れできるようになってます。

WordPressの例

f:id:aftercider:20190316234338p:plain

WordPressはPHPで開発されており、データベース管理システムとしてMySQLを利用している。
そのため動作環境として、最低でも以下を構築する必要がある。

- Apache(Webサーバーソフトウェア)
- MySQL(データベース)
- PHP(プログラミング言語)

Dockerを使わない一般的な構築手順

XAMPPを使ってローカル環境にWordPressをインストールする方法

  • ApacheをまずOSにいれる(多くの場合XAMPP)
  • PHPの指定バージョンが使えるように環境を設定する
  • ファイアーウォールの設定を変更する
  • MySQLをインストールする
  • MySQL上に、WordPress用のDBを作る
  • WordPressをApacheのフォルダにおく
  • MySQLのDB・ユーザー・パスワード・ホストを設定する
  • ブラウザでApacheで解放されているポートにアクセスする

っていう手順を、一つ一つミスがないように実施する必要があります。

また、ApacheのバージョンやMySQLのバージョンについて、知識を持って気をつけないと他の開発環境を汚染してしまいます。

さらに、お試しが終わった後にアンインストールする際に、Apache・MySQL・WordPress・ファイアーウォールの設定などを全て元に戻す必要があってとても大変です。(そして多くの場合、戻すほうが大変)

学生時代、言われるがままにXAMPPいれて、環境を戻せずOS再インストールした記憶があります。

といった感じで、ただWordPressのCMSの触りごごちや見た目を試してみたいだけなのに、かなりのリスクと手間をかける必要が出てきてしまいます。

Dockerを使う方法

  • Dockerを入れる https://www.docker.com/
  • どっかに適当なフォルダを作る
  • そのフォルダに以下をコピペしたdocker-compose.ymlというファイルを作る
  • shellでそのフォルダに入って、 docker-compose up -dを実行
  • ブラウザでlocalhost:8000にアクセスする
version: '3.3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}

これだけで、WordPressを以下のように試せるようになります。

Dockerが入っている環境だったら、1分もかかりません。

f:id:aftercider:20190316234020p:plain
wordpress

さらに、お試しがおわって環境を捨てたいときは、 docker-compose downをshellで実行すれば、WordPressもMySQLもApacheも丸ごと消えてくれるので安心。

コンテナは手元の環境と独立しているので、手元のMySQLやPHPのバージョンの設定にも全く影響がないので、3秒くらいですんなりお別れできるようになってます。

WordPress以外を試すとき

大体の有名なサービスは誰かがdocker-compose.ymlを作って置いてあることが多いです。

まとめ

最近話題のサービスを試したり、開発環境構築するときにぜひ使ってみてください!

MeCab+NEologdをDockerで動かす

$
0
0

f:id:aftercider:20190327142647p:plain日本語の分かち書きや形態素解析の定番であるmecabとNEologd(mecab-ipadic-neologd)の組み合わせ。

辞書ファイルであるNEologdは2週間に一回更新されるらしいですし、インストールするのも手順が多いため、コンテナで動かせるようにします。

Dockerを使って動かすことで、常に最新のNEologdの辞書ファイルを使うことができるという点もメリットです。

やること

使うコンテナ

intimatemerger/mecab-ipadic-neologdをつかいます。

https://hub.docker.com/r/intimatemerger/mecab-ipadic-neologd

レポジトリはこちら。

GitHub - IntimateMerger/dockerfile-mecab-ipadic-neologd: MeCab Docker image with mecab-ipadic-neologd

このDockerImageを提供してるのは日本のこちらの会社です。 corp.intimatemerger.com

コンテナ建て方

pullして対話モードで起動するだけでOKです。楽チン!

# ImageのPull
docker pull intimatemerger/mecab-ipadic-neologd
# 対話モードで起動
docker run -it intimatemerger/mecab-ipadic-neologd mecab

(備考)コンテナの抜け方

※Ctrl+Cでは抜けられないので注意!

  • ログアウトするとき:Ctrl+D (コンテナは終了する)
  • デタッチするとき: Ctrl+P -> Ctrl+Q (コンテナは生き続ける)

つかいかた

こちらのページの一部の文章をかけます。

田村ゆかり - Wikipedia

ラジオなど、音声メディアの前では「ゆかり」という一人称を使っている。
親しい間柄の人物と会話する場合は「あたし」を使う。
雑誌などの活字メディアで使われる一人称は「私」。

対話モードでの使用

docker run -it 〜で起動した後、上の原文をそのままペーストすれば結果が返り値として出てきます。

こんなかんじ。

$ docker run -it intimatemerger/mecab-ipadic-neologd mecab
ラジオなど、音声メディアの前では「ゆかり」という一人称を使っている。
親しい間柄の人物と会話する場合は「あたし」を使う。
雑誌などの活字メディアで使われる一人称は「私」。
ラジオ   名詞,一般,*,*,*,*,ラジオ,ラジオ,ラジオ
など  助詞,副助詞,*,*,*,*,など,ナド,ナド
、 記号,読点,*,*,*,*,、,、,、
音声  名詞,一般,*,*,*,*,音声,オンセイ,オンセイ
メディア    名詞,一般,*,*,*,*,メディア,メディア,メディア
の 助詞,連体化,*,*,*,*,の,ノ,ノ
前 名詞,副詞可能,*,*,*,*,前,マエ,マエ
で 助詞,格助詞,一般,*,*,*,で,デ,デ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
ゆかり   名詞,一般,*,*,*,*,ゆかり,ユカリ,ユカリ
」 記号,括弧閉,*,*,*,*,」,」,」
という   助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
一人称   名詞,一般,*,*,*,*,一人称,イチニンショウ,イチニンショー
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使っ  動詞,自立,*,*,五段・ワ行促音便,連用タ接続,使う,ツカッ,ツカッ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
いる  動詞,非自立,*,*,一段,基本形,いる,イル,イル
。 記号,句点,*,*,*,*,。,。,。
EOS
親しい   形容詞,自立,*,*,形容詞・イ段,基本形,親しい,シタシイ,シタシイ
間柄  名詞,一般,*,*,*,*,間柄,アイダガラ,アイダガラ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
人物  名詞,一般,*,*,*,*,人物,ジンブツ,ジンブツ
と 助詞,格助詞,一般,*,*,*,と,ト,ト
会話  名詞,サ変接続,*,*,*,*,会話,カイワ,カイワ
する  動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
場合  名詞,副詞可能,*,*,*,*,場合,バアイ,バアイ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
あたし   名詞,代名詞,一般,*,*,*,あたし,アタシ,アタシ
」 記号,括弧閉,*,*,*,*,」,」,」
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使う  動詞,自立,*,*,五段・ワ行促音便,基本形,使う,ツカウ,ツカウ
。 記号,句点,*,*,*,*,。,。,。
EOS
雑誌  名詞,一般,*,*,*,*,雑誌,ザッシ,ザッシ
など  助詞,副助詞,*,*,*,*,など,ナド,ナド
の 助詞,連体化,*,*,*,*,の,ノ,ノ
活字  名詞,一般,*,*,*,*,活字,カツジ,カツジ
メディア    名詞,一般,*,*,*,*,メディア,メディア,メディア
で 助詞,格助詞,一般,*,*,*,で,デ,デ
使わ  動詞,自立,*,*,五段・ワ行促音便,未然形,使う,ツカワ,ツカワ
れる  動詞,接尾,*,*,一段,基本形,れる,レル,レル
一人称   名詞,一般,*,*,*,*,一人称,イチニンショウ,イチニンショー
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
」 記号,括弧閉,*,*,*,*,」,」,」
。 記号,句点,*,*,*,*,。,。,。
EOS

ファイルでの入出力(指定フォルダをコンテナにマウント)

実際のユースケースでは対話モードだけでなく、テキストファイルに対して行うことも多々あると思います。 その時は、以下のように実行します。

処理したいテキストファイルがあるフォルダをコンテナのボリュームにマウントして、入出力ファイルを指定するっていうのをやっています。

# わたしの環境でのサンプル
$ docker run -v /Users/aftercider/Documents:/home intimatemerger/mecab-ipadic-neologd mecab  /home/input.txt -o /home/output.txt
$ cat output.txt 
ラジオ   名詞,一般,*,*,*,*,ラジオ,ラジオ,ラジオ
など  助詞,副助詞,*,*,*,*,など,ナド,ナド
、 記号,読点,*,*,*,*,、,、,、
音声  名詞,一般,*,*,*,*,音声,オンセイ,オンセイ
メディア    名詞,一般,*,*,*,*,メディア,メディア,メディア
の 助詞,連体化,*,*,*,*,の,ノ,ノ
前 名詞,副詞可能,*,*,*,*,前,マエ,マエ
で 助詞,格助詞,一般,*,*,*,で,デ,デ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
ゆかり   名詞,一般,*,*,*,*,ゆかり,ユカリ,ユカリ
」 記号,括弧閉,*,*,*,*,」,」,」
という   助詞,格助詞,連語,*,*,*,という,トイウ,トユウ
一人称   名詞,一般,*,*,*,*,一人称,イチニンショウ,イチニンショー
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使っ  動詞,自立,*,*,五段・ワ行促音便,連用タ接続,使う,ツカッ,ツカッ
て 助詞,接続助詞,*,*,*,*,て,テ,テ
いる  動詞,非自立,*,*,一段,基本形,いる,イル,イル
。 記号,句点,*,*,*,*,。,。,。
EOS
親しい   形容詞,自立,*,*,形容詞・イ段,基本形,親しい,シタシイ,シタシイ
間柄  名詞,一般,*,*,*,*,間柄,アイダガラ,アイダガラ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
人物  名詞,一般,*,*,*,*,人物,ジンブツ,ジンブツ
と 助詞,格助詞,一般,*,*,*,と,ト,ト
会話  名詞,サ変接続,*,*,*,*,会話,カイワ,カイワ
する  動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
場合  名詞,副詞可能,*,*,*,*,場合,バアイ,バアイ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
あたし   名詞,代名詞,一般,*,*,*,あたし,アタシ,アタシ
」 記号,括弧閉,*,*,*,*,」,」,」
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使う  動詞,自立,*,*,五段・ワ行促音便,基本形,使う,ツカウ,ツカウ
。 記号,句点,*,*,*,*,。,。,。
EOS
雑誌  名詞,一般,*,*,*,*,雑誌,ザッシ,ザッシ
など  助詞,副助詞,*,*,*,*,など,ナド,ナド
の 助詞,連体化,*,*,*,*,の,ノ,ノ
活字  名詞,一般,*,*,*,*,活字,カツジ,カツジ
メディア    名詞,一般,*,*,*,*,メディア,メディア,メディア
で 助詞,格助詞,一般,*,*,*,で,デ,デ
使わ  動詞,自立,*,*,五段・ワ行促音便,未然形,使う,ツカワ,ツカワ
れる  動詞,接尾,*,*,一段,基本形,れる,レル,レル
一人称   名詞,一般,*,*,*,*,一人称,イチニンショウ,イチニンショー
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
「 記号,括弧開,*,*,*,*,「,「,「
私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
」 記号,括弧閉,*,*,*,*,」,」,」
。 記号,句点,*,*,*,*,。,。,。
EOS

感想

  • 手元のpython環境汚さなくていいのは楽チン。
  • 分かち書きがとても簡単!

Tenka1 Programmer Beginner Contest 2019の参加

$
0
0

f:id:aftercider:20190422130013p:plain

atcoder.jp

https://atcoder.jp/contests/tenka1-2019-beginner

2つしか解けなかった〜〜〜。残念。 Cは問題の意味を間違って、最初完全に違う実装してたのがもったいなかった。 解説ほどスマートではなかったにしろ、あと少しまで行ったので悔やまれる。

DはDPだろうなーと思いつつも、どこをDPにするかがわからず時間切れ。 とりあえずTLEはするけどDPなしのコード作るまでは行った。

全部いつも通りJSで書いた。

A問題

チャチャッと終わらせた。

function Main(input) {
    const A = parseInt(input[0])
    const B = parseInt(input[1])
    const C = parseInt(input[2])
    
    if((A < C && C < B) || (B < C && C < A)) {
        console.log("Yes");
    } else {
        console.log("No");
    }
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

B問題

正規表現使うかなーと思ったけど、調べるの面倒だったのでこれでやった。

function Main(input) {
    var result;
    const N = parseInt(input[0])
    const S = input[1]
    const K = parseInt(input[2])-1
    
    const target = S.charAt(K);
    var ret = "";
    for(var i = 0; i < N; i++){
        ret += S.charAt(i) == target ? target : "*";
    }
    console.log(ret);
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

C問題

変に配列を分割・整理して、そこの実装ミスに時間を取られてしまった。 これは悔しいやつだった。 下のコードはWAでる状態。

function Main(input) {
    const N = parseInt(input[0])
    const S = input[1]
    
    var BW = [];
    if(S.charAt(0) == "."){
        BW.push(0);
    }

    var prev = S.charAt(0);
    var count = 0;
    for (var i = 0; i < N; i++) {
        const element = S.charAt(i);
        if(element == prev){
            count++
        } else {
            BW.push(count);
            prev = element;
            count = 1;
        }
    }
    if(count > 0) {
        BW.push(count);
    }
    if(BW.length %2 > 0){
        BW.push(0);
    }

    var min = Number.MAX_SAFE_INTEGER;
//    console.log(BW);

    for (var i = 0; i < BW.length/2 + 1; i++) {
        var sum = 0;
        for (var j = 0; j < BW.length/2; j++) {
            sum += i <= j ? BW[j*2+1]: BW[j*2];
        }
        
        min = Math.min(sum, min);
    }

    console.log(min);
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

D問題

TLEと一部WAが出るのを潰す時間がなかったやつ。 これがすんなり解けるようになったらかっこいいな〜。

function canBuild(a, b, c) {
    return (a + b > c && b + c > a && c + a > b);
}

function check(r, g, b, As, index, memo) {
    if (As.length == index) {
        return canBuild(r, g, b) ? 1 : 0;
    } else {
        var rgb = [r, g, b];
        rgb.sort((a, b) => { return a - b; });
        var key = "" + rgb[0] + "-" + rgb[1] + "-" + rgb[2] + "-" + index;
        if (typeof memo[key] != "undefined") {
            return memo[key];
        } else {
            const a = As[index];
            var result = (
                check(r + a, g, b, As, index + 1, memo) 
            + check(r, g + a, b, As, index + 1, memo) 
            + check(r, g, b + a, As, index + 1, memo));
            memo[key] = result;
            return result;
        }
    }
}

function Main(input) {
    const N = parseInt(input[0])
    var As = [];
    input.slice(1).forEach(element => {
        const value = parseInt(element);
        As.push(value);
    });
    var memo = {};

    console.log((check(As[0], 0, 0, As, 1, memo) * 3) % 998244353);
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

次の参加時はもっと解けるようになっておきたいな。


AtCoder Beginner Contest 125 にエア参加して

$
0
0

f:id:aftercider:20190428202214p:plain

AtCoder Beginner Contest 125に参加!するつもりだったけど、家庭の事情により今回は参加できず。

あと、EテレのSWITCHインタビューもみなければならなかった。Clusterの加藤さんが出た回。

f:id:aftercider:20190428195950p:plain
SWITCHインタビュー 達人達(たち)「諏訪部順一×加藤直人」

ABC125

f:id:aftercider:20190428195618p:plain

コンテスト後のエア参加だったけど、4問とも難なく回答できた。

C問題

最大公約数っていう、C問題が結構難しそうな雰囲気出してた。

ただ、計算量が線形に落ち着いてたのでいけるかなー?

と思って出したのが無事ACだったのでよかった。

function checkAll(div, As) {
    var mod = 0
    var result = true;
    As.forEach(element => {
        if (element % div > 0) {
            mod++;
            if (mod > 1) {
                result = false;
            }
        }
    });
    return result;
}

function Main(input) {
    const N = parseInt(input.shift());
    var As = input.map(x => parseInt(x));

    As = As.sort((a, b) => { return a - b; }); // 昇順ソート

    var firstDiv = 0;
    var secondDiv = 0;

    for (var div = As[0]; div > 0; div--) {
        if (As[0] % div == 0) {
            // 約数なら
            if (checkAll(div, As)) {
                firstDiv = div;
                break;
            }
        }
    }

    for (var div = As[1]; div > firstDiv; div--) {
        if (As[1] % div == 0) {
            // 約数なら
            if (checkAll(div, As)) {
                secondDiv = div;
                break;
            }
        }
    }

    console.log(Math.max(firstDiv, secondDiv));
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

D問題

CとDは入れ替えてもよさそうな難易度だった。

function Main(input) {
    const N = parseInt(input.shift())
    const As = input.map(x => parseInt(x));

    var isOdd = false;
    var absSum = 0;
    var minAbs = Number.MAX_SAFE_INTEGER;

    As.forEach(element => {
        if (element < 0) {
            isOdd = !isOdd;
            element *= -1;
        }
        absSum += element;
        minAbs = Math.min(minAbs, element);
    });

    if (isOdd) {
        absSum -= minAbs * 2;
    }

    console.log(absSum);
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));```

# 感想

かなりスムーズにとけたとはいえ、30分くらいはかかってて、順位上位の人たちって何者なんだろうかと謎が深まるABC125だった。

あと、最大公約数・最小公倍数ってのは解く定石があるんですね・・・しらなかった。


[http://tota66.hatenablog.com/entry/2019/04/27/235849:title]


[https://qiita.com/tawatawa/items/408b872a7092be0d7b3c:title]

新しくWindowsPCを組みました

$
0
0

WindowsPC

久しくMacを使っていた私ですが、最近GPUを使っていろいろやろうと思ったときにどうしてもMacだと厳しいことがあり、とうとうWindowsPCを新調しました!

今回、強力な友人に組んでもらい、コスパ抜群のPCを作っていただきました。

自分に課せられたGivenConditionとしては、GTX1070Tiがたくさんあるので、それを必ず使うこととしました。

構成

CPU

Intel Core i5-9400F 2.9GHz

GPUは先述の通り、立派な1070Tiがあるので、CPUはグラフィック能力なしで問題なしでした。

6コアなので、たいていのことでは困ることはなしです。

メモリ

16GBにしました(8GBx2)。 詳しい型番はメモってないけども、友人とTSUKUMOの方のアドバイスに従って選択。

ストレージ

SSD(NVMe)の500GBにしました! これも型番は不明で、おすすめのものを選択。

GPU

「MSI GeForce GTX 1070 Ti GAMING 8G」で確定。

2枚刺しもおすすめされたけど、用途考えると1枚刺しで十分だったので1枚に抑えた。

MSI GeForce GTX 1070 Ti GAMING 8G グラフィックスボード VD6490

MSI GeForce GTX 1070 Ti GAMING 8G グラフィックスボード VD6490

ケース

NZXT製のミドルタワーH500にしました!(色はホワイト) GPU入るサイズっていうのと、CPUファンを大きいのにして静かに運用したかったので、こちらにしました。 (一番大きかったのは友人からお古で売ってもらえるってところですが!)

NZXT H500 ネイキッドモデル [ White & Black ] CA-H500B-W1

NZXT H500 ネイキッドモデル [ White & Black ] CA-H500B-W1

触ってみた感じ

VRChat x HTC VIVE

何ら重いところはない快適!

Cluster x HTC VIVE

何ら重いところはない快適!

Cities Skylines

Macで描画が追い付いていないくらい、町がでかくなっていましたが、今回のPCだとサクサク! 画面移動も超快適でした。

WSL, Docker

MacだとDockerつかってて、Windowsは8くらいからほとんどメールとパワポくらいしか触ってなかったので詳しくは知らなかったんですが、 WSLってのが便利ですね・・・

とりあえずUbuntuいれときました。

ただ、開発でしょっちゅうDockerをつかってたのもあって、その辺をどう扱っていくかが軽く調べた感じ難しそうなので、もう少し調べておく必要がありそうです。

明日は輝夜月ライブ

このために新調したといっても過言ではない!

楽しみだ~!!!

輝夜月ライブ@ZeppVR2にVRで参加した! #カグヤルナライブ

$
0
0

f:id:aftercider:20190501225505p:plain

令和最初のライブは、輝夜月VRライブ!

VR体験ができるWindowsPCとHTCVIVE、そしてVRチケットを確保したので、参加してきました~!

日清焼そばU.F.O. presents「輝夜 月 LIVE@ZeppVR 2」

cluster.mu

2017年12月9日にYouTubeデビューして以来、破壊的かつユーモア溢れるトークとナチュラルボーンでチャーミングなキャラクターを武器に、凄まじい勢いで人気を集めているバーチャルユーチューバー、輝夜 月(カグヤルナ/Kaguya Luna)。

昨年行った世界初となるVRライブ「LIVE@Zepp VR」に引き続き、仮想空間「Beyond the Moon」に在る 「Zepp VR(ゼップブイアール)」 にてVRライブが開催決定!!

新年号初日となるメモリアルなこの日に、輝夜 月による新たな挑戦をお楽しみに!

≪概要≫ 日 程 2019年5月1日(水・祝) 時 間 開場 19:00 / 開演 19:30

感想

HMD体験はVRChatやCluster内でのイベントをはじめ、多数体験していたんですが、VRライブは今回初めて。 VTuberライブとしては、でろーんの大阪ライブをニコニコで見ていたので2回目な感じです。 感想としては月並みですが、すっごいよかった! 以下、それぞれよかったところについて書いていきます。

ライブ中の演出

f:id:aftercider:20190501225539p:plain

VR空間は何でもできるけど、何でもやると何が何だか分からなくなるので、そこが難しいところ。

今回の輝夜月ライブは2回目ということもあるためか、かなりそのバランスが良かったようで、イベントに収集しながらも、演出でおぉっ!っと思わせてくれるような瞬間がとてもたくさんありました。

コンサートシーン

f:id:aftercider:20190501225634p:plain

歌のシーンはおそらく録画?(シーンが切り替わる感じから推測)だと思うんですが、モーションも表情もとてもかわいい!

見てるこっちも楽しくなってくるコンサートでした。

しかもVRだとみんな最前列に行くんですね。ちょっと引きで写真撮ってみるとこんな感じでなかなか面白い感じでした。

f:id:aftercider:20190501225652p:plain

歌詞のオーバーレイ

f:id:aftercider:20190501225710p:plain

歌詞がタイポグラフィ・モーショングラフィックスのように歌と一緒に出てくる、しかも空間中に!

すごくかっこいい演出になってました、これはすげぇぇぇぇ!ってなりました。

テキスト出すだけでなく、空間を区切ったり広げたり移動したり、そういった動きのある演出はすごく魅力的なんだなと感じることができました。

巨大スクリーン

f:id:aftercider:20190501225729p:plain

ライブ中の超でかいスクリーン演出はおぉ~となりました。

コンサート会場だとなかなかこんなスクリーン出せないし、動かすことなんてなおさら無理ですが、VRだとできちゃうんですね!

HTCVIVE使ってたんですが、表情をもっとよく見るには解像度がもうちょっとほしいなーと思っていたところだったので、とてもよかったです。

トークシーン

f:id:aftercider:20190501225811p:plain

常日頃、輝夜月の動画は見てるんですが、その雰囲気そのまんまで大満足でした。

会場がVR・ZeppDivercity・映画館ライブビューイングと3カテゴリに分かれていたこともあり、どうつないでくるんだろう?と思っていましたが、それぞれの空間に呼び掛けたり、状況を伝えてくれたりしていたので、みんな楽しんでいるんだなってことが感じられました。

本人的にはVRでの視聴を推しているみたいなので、今後VR視聴者の枠が増えてくれるといいなと感じました。

トークでのハグ

f:id:aftercider:20190501225845p:plain

ステージは当然一般視聴者は入ることができないのですが、逆に輝夜月は一般ユーザーのエリアに入ることができるそうで、トークの途中でなんと降りてきてくれました!

しかも、視聴者とハグっていうことで、いろんな場所に立ってハグしてくれていて、自分もハグしに行きましたがなんだか特別な感じがして、これいいものだ・・・と感慨深かったです。

音響

「ゆーても音声はライブ会場じゃないと大したことないんやろ~?」と思っていましたが、実際VR空間で聞いてみると大迫力!!

いい音&いい音響が見事に構成されていて、圧巻でした。

立体音響について、ここ数年懐疑的だったんですが、こういった形で体験すると非常に大事な技術なんだなとしみじみ感じました。

終わった後

f:id:aftercider:20190501230202p:plain

コンサート後は、いつものClusterと同じくユーザーの書き込みがタイムライン上で見えるようになってるんですが、これがコンサート後だとすっごくうれしい!

ライブ後にやっぱりこう誰かと語り合いたいものですが、VRだとなかなかそうはいかないし、SNSに書き込もうとHMD外してしまうと外した瞬間に興奮が冷めてしまいそうなんですが、その課題をうまく解決してくれていました。

f:id:aftercider:20190501230405p:plain

あと、記念撮影スポットがあるのはうれしかったです!

やっぱり自分がライブ行ったっていう証は残したいので、それが残せる会場構成&Clusterの機能は素晴らしいなーと感じました!!

まとめ

次回のVRライブもまたぜひ参加したい!

AtCoder Grand Contest 033 に参加して

$
0
0

f:id:aftercider:20190428202214p:plain

AtCoder Grand Contest 033(略称AGC033?)に参加しました。

atcoder.jp

AGC033

f:id:aftercider:20190504235029p:plain

50分おくれでの参加だったんですが、結果はABの2完でした。

AにTLEで結構手こずってしまった一方、Bは実装中に閃いて比較的にすぐおわりました。

残り時間でDを解こうと思ったんですが、足りなかったですね・・・

A問題

とりあえず「毎回全探索でどのくらいのもんだろう?A問題だしいけるんじゃない?」と思って、ひたむきに実装しましたが、ABCのA問題とは違いましたね・・・

あえなくTLEでダメでした。

ということで、回答にもあった通り、チェックするマスを絞って塗っていく手法を採用してAC。

実装はとても汚い感じ(涙)

function getCell(w, h, input, W, H) {
    return input[Math.max(0, Math.min(h, H - 1))][Math.max(0, Math.min(W - 1, w))];
}

function insert(w, h, input, W, H, value) {
    input[Math.max(0, Math.min(h, H - 1))][Math.max(0, Math.min(W - 1, w))] = value;
}

function checkValid(w, h, W, H) {
    return h == Math.max(0, Math.min(h, H - 1)) && w == Math.max(0, Math.min(W - 1, w));
}

function Main(input) {
    const H = parseInt(input.shift());
    const W = parseInt(input.shift());

    isBlack = false;
    var count = 0;

    var currentMap = [];
    var targetMap = [];
    input.forEach(element => {
        currentMap.push(element.split(""));
    });

    for (var h = 0; h < H; h++) {
        for (var w = 0; w < W; w++) {
            var cell = getCell(w, h, currentMap, W, H);
            if (cell == "#") {
                if (checkValid(w - 1, h, W, H) && getCell(w - 1, h, currentMap, W, H) == ".") targetMap.push([w - 1, h]);
                if (checkValid(w + 1, h, W, H) && getCell(w + 1, h, currentMap, W, H) == ".") targetMap.push([w + 1, h]);
                if (checkValid(w, h + 1, W, H) && getCell(w, h + 1, currentMap, W, H) == ".") targetMap.push([w, h + 1]);
                if (checkValid(w, h - 1, W, H) && getCell(w, h - 1, currentMap, W, H) == ".") targetMap.push([w, h - 1]);
            }
        }
    }

    while (targetMap.length > 0) {
        var nextTarget = [];
        var lightTarget = [];
        targetMap.forEach(element => {
            if (getCell(element[0], element[1], currentMap, W, H) == ".") {
                lightTarget.push(element);
            }
            insert(element[0], element[1], currentMap, W, H, "#");
        });
        targetMap = lightTarget;
        targetMap.forEach(element => {
            var w = element[0];
            var h = element[1];
            if (checkValid(w - 1, h, W, H) && getCell(w - 1, h, currentMap, W, H) == ".") nextTarget.push([w - 1, h]);
            if (checkValid(w + 1, h, W, H) && getCell(w + 1, h, currentMap, W, H) == ".") nextTarget.push([w + 1, h]);
            if (checkValid(w, h - 1, W, H) && getCell(w, h - 1, currentMap, W, H) == ".") nextTarget.push([w, h - 1]);
            if (checkValid(w, h + 1, W, H) && getCell(w, h + 1, currentMap, W, H) == ".") nextTarget.push([w, h + 1]);
        });
        count++;
        targetMap = nextTarget;
    }

    console.log(count);
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

B問題

着手して最初、累積和の問題かな?と思って、累積和を作るロジックを書いていました。

途中、子供が泣いて様子を見に行った時に、「あ、これじゃだめだ」と気づき、方針転換。

その結果、シンプルなコードでACになりました。

A問題よりこっちの方が簡単だった感じ。

function Main(input) {
    const H = parseInt(input.shift());
    const W = parseInt(input.shift());
    const N = parseInt(input.shift());
    const sr = parseInt(input.shift());
    const sc = parseInt(input.shift());
    const S = input.shift();
    const T = input.shift();

    var left = sc, right = sc, top = sr, bottom = sr;

    for (var i = 0; i < N; i++) {
        var s = S.charAt(i);
        var t = T.charAt(i);
        if (s == "L") {
            left--;
        } else if (s == "R") {
            right++;
        } else if (s == "U") {
            top--;
        } else {
            bottom++;
        }
        if (left <= 0 || right > W || top <= 0 || bottom > H) {
            console.log("NO");
            return;
        }

        if (t == "L") {
            right = Math.max(1, right - 1);
        } else if (t == "R") {
            left = Math.min(W, left + 1);
        } else if (t == "U") {
            bottom = Math.max(1, bottom - 1);
        } else {
            top = Math.min(H, top + 1);
        }
    }
    console.log("YES");
}

// 改行・空白で分割
Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

感想

AGCは難しい。。。

ただ、やりごたえはあるので、過去問やってみよう。

AtCoder Beginner Contest 128 に参加して

$
0
0

f:id:aftercider:20190428202214p:plain

AtCoder Beginner Contest 128(略称ABC128)に参加しました。

atcoder.jp

ABC128

今回も安定の20分おくれで遅刻スタート。結果はABCの3完でした。

A問題

書くだけ。

B問題

ソート順の設定と、ソート後のインデックスの保持だったので、これも書くだけ。

function Main(input) {
    var result;
    const N = parseInt(input.shift());
    const SP = [];
    for (var i = 0; i < N; i++) {
        SP.push({
            I: i + 1,
            S: input[i * 2],
            P: parseInt(input[i * 2 + 1])
        })
    }

    SP.sort((a, b) => {
        var stComp = a.S.localeCompare(b.S);
        if (stComp == 0) {
            return b.P - a.P;
        }
        return stComp;
    })
    SP.forEach(value => console.log(value.I));
}

Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n|\s/));

C問題

条件結構絞ってあるので、全探索いけそうと思って取り組んだけども、バリエーション作る関数が思ったより作るの大変だった。。。

あと、毎回やってしまうところで、インデックスを1スタートとすべきところを0のままやってしまい、タイムロス。。。

function Main(input) {
    var result;
    const N = parseInt(input[0].split(/\s/)[0]);
    const M = parseInt(input.shift().split(/\s/)[1]);
    const KS = input.slice(0, M).map(value => value.split(/\s/).map(v => parseInt(v)));
    const P = input.slice(M)[0].split(/\s/);

    var count = 0;
    generateMap(N).forEach(SW => check(SW, KS, P) == true ? count++ : 0);
    console.log(count);
}

function generateMap(N) {
    var arr = [];
    for (var index = 0; index < Math.pow(2, N); index++) {
        var value = index;
        var insert = [];
        for (var i = 0; i < N; i++) {
            insert.push(value % 2);
            value = Math.floor(value / 2);
        }
        arr.push(insert);
    }
    return arr;
}

function check(sw, KS, P) {
    for (var index = 0; index < KS.length; index++) {
        var ks = KS[index];
        var check = 0;
        for (var i = 1; i < ks.length; i++) {
            var s = ks[i] - 1;
            if (sw[s] == 1) check++;
        }

        if (P[index] == "1") check++;
        if (check % 2 > 0) return false;
    };
    return true;
}

Main(require("fs").readFileSync("/dev/stdin", "utf8").trim().split(/\n/));

D問題

大体の方針は立ったので、あとは実装するだけだとなったけど、スムーズに実装ができず、時間切れ。

定番の関数をストックしておくといいのかな・・・

解答見たら、方針はあっていたのでその点はよかった。

感想

今回のコンテストでとりあえず茶色コーダーになった。

コンテストの問題も難しいけど、一番難しいのはコンテスト開催の時間にPCの前にいることだな・・・

毎度思うことだけど、コンテストで使うNodeJSのバージョンを今のv5.12からv10あたりまで上げてほしい。。。

Viewing all 129 articles
Browse latest View live