えいやで会社をやめたらどうなるブログ

SwiftやGolangを中心に書いていくフリーランスエンジニアのブログ。

名作ボードゲーム「カタン」で中級者になるための4つの意識

こんにちは、がわわです。
最近ドミニオンブロックス、そしてカタンをやっていて熱が高まり思わず記事を書きたくなって今に至ります。技術ブログなのに……。
この記事はルールを覚えてそこそこ遊んだ初級者を対象としています。
中級者になりたいけれど、いまいちわからない、という方に役立てば良いと思っています。
僕は自称中級者で、この記事は経験から語っているので根拠があるものではありません。悪しからず。

さて、自称中級者として、中級者になるために必要なことは以下の4つの意識なのではないかと思っています。

  1. 「10点」への意識
  2. 「交渉」への意識
  3. 「港」への意識
  4. 「カード」への意識

意識を難易度順に並べたつもりです。順番に見ていきましょう。

1.「10点」への意識

ルールも覚えて、「なるほどやっぱり麦がないと勝てないな〜」くらいに思い始めた初級者。
5~7点くらいは取れるんだけどうまい人には勝てない。
なんだよ運ゲーかよカタンってぼやいている。

足りないものは「10点」への意識なのではないかと思っています。
拡張版とかでないと基本的にカタン
10点を取らないと勝てないゲームです。

漫然と道を伸ばして開拓地ばかり建てて「都市にならない……」とか言ってたらダメです。
10点になるには

  1. 都市 * 3 + 騎士王 + 勝利点や開拓地で2点
  2. 都市 * 2 + 開拓地 * 4 + 道王
  3. 都市 * 4 + 開拓地 * 2

あたりがあり得る戦略なのではないしょうか。
もちろん、全部を網羅してないですし、カードや開拓地、都市の数にはぶれがあります。
要は「騎士王」「道王」「どっちもなくて頑張る」ということです。

1はとてもわかりやすいです。
基本的に麦鉄羊を持っていればカードを引いて騎士王を目指して相手を妨害し、
港を得たタイミングで都市化、という単純かつ強い戦略だと思います。

2は木土で道を伸ばして開拓地を建てていく、地味ですがこれもわかりやすい戦略です。
後半に鉄が必要なので、木港や土港は押さえておきたいですね。

3は序中盤で騎士王狙い、道王狙いの人たちに差をつけられている時に仕方なく取らなければならない戦略です。
あまり勝率は良くなさそうですが、騎士王・道王が取れないからと言って腐らないことが大事だと思います。
交渉したり港を押さえたりで目立たずじわじわ逆転を目指しましょう。

2.「交渉」への意識

初級者の頃は4:1貿易を考えてしまいがちです。
ですが、4:1貿易は効率が悪いです。
なので、がんがん交渉してみましょう。
全体的に場を見て、どの資源が薄くて、プレイヤーによって必要そうな資源は何か、
そこらへんを考えて「お互いに得をする」交渉を心がけましょう。

ただし、交渉は相手ありきのものです。
それだけに依存しては予想外に出遅れたりするし、
中盤では港を押さえているプレイヤーはもう交渉してくれなかったりもするので注意しましょう。

3.「港」への意識

あった方が良いものです。港がないということはダイスの目か交渉か4:1貿易でしか欲しい資源は得られません。
満遍なく資源を取れる配置になっていたら良いと思うかもしれませんが、そんなにゲームはうまく進みません。
なかなか鉄がでてこない……とかはよくあることでしょう。
そして4:1貿易を期待しすぎると手札が増えてバーストの危険性も高まります。

おすすめは2:1港でしょう。
なので、初期配置の時点で自分が得られそうな資源とそれに関する港の位置は押さえておきましょう。
そして、初期配置の道は港の方向に伸ばしておいて、3軒目の開拓地は港に建てるのがわかりやすいのではないでしょうか。

ただし、港を持っているプレイヤーは速いので警戒されがちです。注意しましょう。

4.「カード」への意識

初級者の頃はカードはなんとなく資源が余っているから引くもの、という風に思いがちです。
しかし、カードは強いです。絶対に覚えるべきです。
以下は発展カード25枚の内訳です。

  1. 騎士 * 14
  2. 勝利点 * 5
  3. 街道建設 * 2
  4. 収穫 * 2
  5. 独占 * 2

順に見ていきましょう。

1.騎士

これは14 / 25の確率で引けます。強い理由として盗賊を好きなタイミングで動かせることです。
自分の資源の上に盗賊が乗っていてもそれをはねのけて相手を封殺できる。
それがたったの麦鉄羊が1つずつで良い。相手から1資源奪えるから実質2資源でいい。

さらに、3枚集めれば騎士王で2点です。実質2 / 3点です。

最後に、道王と違って騎士王は奪い返されづらい。
なぜなら、追いかけてきたプレイヤーに盗賊を置いてやれば良いからです。

2.勝利点

勝利点を引くと「なんだ、勝利点か」ってなっている人も多そうですが、たったの麦鉄羊が1つずつで1点が得られます。
開拓地だって4資源なので、勝利点は3資源で良いので効率が良いといえます。

3.街道建設

カード戦術だとあまり要らないカードですが、開拓地を作るための特急券くらいに思うと良いのではないでしょうか。

4.収穫

カードでは一番弱そう。でも好きな資源を2つなので、中盤に困った時の使い勝手の良いカードだと思いましょう。

5.独占

友達をなくすカード。終盤で逆転の秘策になりうる強いカード。
終盤になると都市を建てているプレイヤーも多いので、場全体で手札の枚数も増えているため独占で得られる資源の数は5~10くらいだと考えているとその強さがわかるでしょう。
序盤では4:1貿易を考えてこつこつ集めているプレイヤーからむしり取って封殺し、速攻で逃げ切るというのもあるのかもしれません。見たことはないですが。

ちなみに交渉からの独占は友達をなくすので注意しましょう。
どういうことかというと

A「俺の麦2とお前の鉄2を交換しようぜ」
B(よし、これで俺も都市が建てられるぞ……)
(交換後)
A「麦を独占する」
B(こいつ、俺の鉄2をむしり取ったあげく場全体から麦を回収するだと……)

ってことです。
ルール上問題ありません。http://www.catan.jp/faq-page/monopole/
が、世界選手権(http://www.gp-inc.jp/catan/catan2012j/world.html)でも最悪の空気になったそうですし、
よっぽど勝ちにこだわらなければならない場以外ではやめたほうが良いです。

まとめ

中級者になるために必要な4つの意識

  1. 「10点」への意識
  2. 「交渉」への意識
  3. 「港」への意識
  4. 「カード」への意識

10点を意識しつつ港をどのタイミングでどこに建てるか、
そしてカード戦術をとるのか道王を目指すのか、
そんな風にいろいろと考えて、時には臨機応変にゲーム途中で戦略を変えられると中級者といっても良いと思っています。

目指せ、中級者!

AutoLayoutとかそれにまつわるライフサイクルでの気づき

【AutoLayout】

  • 自分の端末のサイズを手っ取り早く確認したかったらViewControllerの右から3番目のタブからSimulated Metricsを選択して、自分の端末のサイズを選択
  • SubViewをViewに合わせて調整したいとき:
    • SubViewを選択し、Controlを押しながらViewへドラッグ
    • 例えば中央揃えならCenter Holizontally In Containerを選ぶ
  • 上下左右のConstraintを設定するにはPinから
    • Leading Space = 左
    • Trailling Space = 右
    • Bottom Space = 下
    • Top Space = 上
  • 画面サイズによらない幅や高さもPinからWidth, Heightを選択して固定できる

【ViewControllerのライフサイクル】

  • ViewWillAppear → ViewWillLayoutSubViews → ViewDidLayoutSubViews → ViewDidAppear
  • レイアウト絡みがLayoutSubViews
  • ConstraintはupdateConstraintsが呼ばれたタイミングで更新される
  • つまり、updateConstraints→layoutSubviews
  • よって、viewのframeなどをつかった処理を加えたいときはviewDidLayoutSubviewsで行うのが確実かもしれない

ERROR ITMS-90035とかdsym_upload.shとかInvalid Signatureとかがらみのエラーが出たときの対処法

こんにちは。がわわです。

f:id:gawawa124:20150521105658p:plain

タイトルのようなエラーが出たときのスクショです。
このときに僕がやったのは

Crittercismを抜きました。

ただ、それだけ。
これでSubmit to App Storeできました。

ただ、5.2.0にアップデートすると直る、と聞いたのでアップデートもしてみました。
dsym_upload.shファイルがなくなっていたので、多分Submit to App Storeできるんじゃないですかね。
まだ、試してないですけど。

注意点としては、
Run Scriptのところにdsym_upload.shの参照を外す
ところです。

これをやっていなくてずっとShell script invocation errorが出て困っていたので。
すっぱりRunScriptから削除しちゃいましょう。

以上です。

MultipeerConnectivityの復習

こんにちは、がわわです。
ブログを書くのをだいぶサボってました。Golangでうまくいかずやる気を失っていました。

今日はSwiftの話です。

MultipeerConnectivityとは:
WifiBluetoothで端末間通信を行いやすくするフレームワーク


ざっくり手順:

  • MultipeerConnectivityに必要なオブジェクトたちを用意する
    • MCPeerID:識別子
    • MCSession:セッション
    • MCAdvertiserAssistant:ピアを他のピアから見つけてもらうようにするヘルパークラス
    • MCBrowserViewController:ピアの検索・接続・要求ができる
  • MCSessionのインスタンスがsendDataメソッドを呼び出して送信する
  • 受け取る時はMCSessionのデリゲートメソッドのdidReceiveDataメソッドが呼ばれる

スライスから削除する(deleteみたいな関数はない)

deleteみたいな関数はないようなので自分で書きましょう。

ダメな例

func delete(s []int, i int) []int {
    s = append(s[:i], s[i+1:]...)
    return s
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    s = delete(s, 2)
    //slice:[1 2 4 5], len:4, cap:5
    fmt.Printf("slice:%d, len:%d, cap:%d\n", s, len(s), cap(s))
    //s == [1 2 4 5 5]
    fmt.Println(s[:cap(s)])
}

このままだと見た目は削除できているように見えるんですけど、容量が削除されていないため不要な値が残っているようです。
なので、容量まで削除しましょう。

良い例

func delete(s []int, i int) []int {
    s = append(s[:i], s[i+1:]...)
    //新しいスライスを用意することがポイント
    n := make([]int, len(s))
    copy(n, s)
    return n
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    s = delete(s, 2)
    //slice:[1 2 4 5], len:4, cap:4
    fmt.Printf("slice:%d, len:%d, cap:%d\n", s, len(s), cap(s))
    //s == [1 2 4 5]
    fmt.Println(s[:cap(s)])
}


参考サイト:
Go言語のスライスを理解しよう
Go のスライスの内部実装 - Block Rockin’ Codes

配列に要素があるか確認する方法(containsみたいな関数はない)

containsみたいな関数はないようなので自分で書きましょう。

func contains(s []int, e int) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}

Golangで任意の型にあたるものはinterface{}

こんにちは。がわわです。

自分が詰まった部分は「Google App EngineのDatastoreで*Keyの配列をjsonで返す」というところでした。
以下は、ヘッダーに書かれたユーザーIDのtweet一覧をTweetというカインドから引っ張ってきて、keyとtweetjsonで返す関数を書こうと思ったダメな例です。

ダメな例

func GetTweets(w http.ResponseWriter, r *http.Request){
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Tweet").
		Filter("UserId =", r.Header.Get("X-USERID"))
	var tweets []Tweet
	keys, err := q.GetAll(c, &tweets)
	if err != nil { panic(err) }
	
	//Keyが定義されていないからコンパイルエラー
	response := map[string][]*Key {
		"Keys": keys,
		"Tweets": tweets,
	}
	json, err := json.Marshal(response)
	if err != nil { panic(err) }
	w.Header().Set("Content-type", "application/json")
	w.Write(json)
}

正しい例

	//interface{}に任意の型を入れられる
	response := map[string]interface{}{
		"Keys": keys,
		"Tweets": tweets,
	}

interface{}とはメソッドが何もない空のインターフェースということですが、任意の型を入れられるものだということがわかりました。

参考サイト:
急いで学ぶGo lang#6 インターフェイス | Developers.IO