2011年2月13日 星期日

Do The Simplest Thing That Could Possibly Work

今天加 SphereStack test,果然爛掉。為什麼?這是因為個人奉行:
Do the simplest thing that could possibly work.

這就是 test-driven 的精神,除非測試失敗,否則不要寫多餘的程式碼,即使下個測試會用到。因此原先系統不支援圓形物體,這就是為什麼會爛掉的原因。

在這當下,才是真正寫程式碼的時機。
  if (config.shape_config->type == ShapeConfig::Polygon) {
...
b2PolygonShape shape_impl;
...

b2FixtureDef config_impl;
config_impl.shape = &shape_impl;
config_impl.friction = config.friction;
config_impl.restitution = config.restitution;
config_impl.density = config.density;

itself_->CreateFixture(&config_impl);
}
else if (config.shape_config->type == ShapeConfig::Circle) {
...
b2CircleShape shape_impl;
...

b2FixtureDef config_impl;
config_impl.shape = &shape_impl;
config_impl.friction = config.friction;
config_impl.restitution = config.restitution;
config_impl.density = config.density;

itself_->CreateFixture(&config_impl);
}
好多重複的程式碼,雖然可以立即 refactor,改善既有的程式碼,但目前測試都沒過,千萬別急著動,這也是 test-driven 的精神。測試過了再來 refactor,順便確保 refactored code 是對的。

跑測試,沒過。見鬼了!不是的,因為我又少支援線段物體,只好再加上去讓他過。
  if (config.shape_config->type == ShapeConfig::Polygon) { 
...
}
else if (config.shape_config->type == ShapeConfig::Circle) {
...
}
else if (config.shape_config->type == ShapeConfig::Edge) {
...
b2EdgeShape shape_impl;
...

b2FixtureDef config_impl;
config_impl.shape = &shape_impl;
config_impl.friction = config.friction;
config_impl.restitution = config.restitution;
config_impl.density = config.density;

itself_->CreateFixture(&config_impl);
}
更多重複的程式碼。這不重要,重要的是先跑測試。啊!過了。接著再來 refactor 吧!
  b2Shape* shape_wrapper;

if (config.shape_config->type == ShapeConfig::Polygon) {
...
b2PolygonShape* shape_impl = new b2PolygonShape();
shape_wrapper = shape_impl;
...
}
else if (config.shape_config->type == ShapeConfig::Circle) {
...
b2CircleShape* shape_impl = new b2CircleShape();
shape_wrapper = shape_impl;
...
}
else if (config.shape_config->type == ShapeConfig::Edge) {
...
b2EdgeShape* shape_impl = new b2EdgeShape();
shape_wrapper = shape_impl;
...
}
else {
return;
}

b2FixtureDef config_impl;
config_impl.shape = shape_wrapper;
config_impl.friction = config.friction;
config_impl.restitution = config.restitution;
config_impl.density = config.density;

itself_->CreateFixture(&config_impl);

delete shape_wrapper;
再跑測試,還是過,refactor 品質沒有問題 (可能 code 還很髒,可能還有 bug,不過至少可以過 SphereStack)。Do the simplest thing that could possibly work.

More Acceptance Tests

Box2d Testbed project 藏有許多漂亮的 demos,之前已經完成最簡單的 freefall 測試,這次玩複雜一點的 VaryingFriction 測試:準備五個箱子,摩擦係數皆不同。摩擦係數高 > 0,走的距離短,最後會卡在滑坡上;摩擦係數零的箱子會一直滑下去 (牛頓第一運動定律:動者恆動靜者恆靜)。

這次 test 採用 gtest 的 Test Fixtures,這樣就可以寫各式各樣類似的測試 (僅僅 time_step 會變)。

整個 code 很簡單:
#include <gtest/gtest.h>
#include "physics_simulation/box2d/box2d_service.h"
#include "physics_simulation/box2d/box2d_world.h"
#include "physics_simulation/box2d/box2d_body.h"

#include "physics_simulation/world_config.h"
#include "physics_simulation/body_config.h"
#include "physics_simulation/fixture_config.h"
#include "physics_simulation/polygon_shape_config.h"
#include <math.h>

namespace physics_simulation {
namespace box2d {

class VaryingFrictionTest : public testing::Test {
protected:
virtual void SetUp();
virtual void TearDown();

World* world_;
Body* box_[5];
Body* track_[3];
Body* ground_;

private:
void SetUpWorld();
void SetUpGround();
void SetUpTopTrack();
void SetUpMiddleTrack();
void SetUpBottomTrack();
void SetUpRightStopper();
void SetUpLeftStopper();
void SetUpFiveBoxes();

Body* stopper_[2];
};

void VaryingFrictionTest::SetUp() {
SetUpWorld();
SetUpGround();
SetUpTopTrack();
SetUpMiddleTrack();
SetUpBottomTrack();
SetUpRightStopper();
SetUpLeftStopper();
SetUpFiveBoxes();
}

void VaryingFrictionTest::TearDown() {
delete world_;
}

void VaryingFrictionTest::SetUpWorld() {
Service* service = new Box2dService();

WorldConfig config;
config.gravity.set(0.0f, -10.0f);
this->world_ = service->createWorld(config);

delete service;
}

void VaryingFrictionTest::SetUpGround() {
BodyConfig config;
this->ground_ = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsEdge(math::Vector2(-40.0f, 0.0f),
math::Vector2( 40.0f, 0.0f));
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->ground_->createFixture(fixture);
}

void VaryingFrictionTest::SetUpTopTrack() {
BodyConfig config;
config.position.set(-4.0f, 22.0f);
config.angle = -0.25f;
this->track_[0] = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsBox(13.0f, 0.25f);
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->track_[0]->createFixture(fixture);
}

void VaryingFrictionTest::SetUpMiddleTrack() {
BodyConfig config;
config.position.set(4.0f, 14.0f);
config.angle = 0.25f;
this->track_[1] = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsBox(13.0f, 0.25f);
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->track_[1]->createFixture(fixture);
}

void VaryingFrictionTest::SetUpBottomTrack() {
BodyConfig config;
config.position.set(-4.0f, 6.0f);
config.angle = -0.25f;
this->track_[2] = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsBox(13.0f, 0.25f);
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->track_[2]->createFixture(fixture);
}

void VaryingFrictionTest::SetUpRightStopper() {
BodyConfig config;
config.position.set(10.5f, 19.0f);
this->stopper_[0] = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsBox(0.25f, 1.0f);
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->stopper_[0]->createFixture(fixture);
}

void VaryingFrictionTest::SetUpLeftStopper() {
BodyConfig config;
config.position.set(-10.5f, 11.0f);
this->stopper_[1] = world_->createBody(config);

PolygonShapeConfig shape;
shape.setAsBox(0.25f, 1.0f);
FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 0.0f;
this->stopper_[1]->createFixture(fixture);
}

void VaryingFrictionTest::SetUpFiveBoxes() {
PolygonShapeConfig shape;
shape.setAsBox(0.5f, 0.5f);

FixtureConfig fixture;
fixture.shape_config = &shape;
fixture.density = 25.0f;

const float friction[5] = { 0.75f, 0.5f, 0.35f, 0.1f, 0.0f };
for (int i = 0; i < 5; ++i) {
BodyConfig config;
config.type = BodyConfig::Dynamic;
config.position.set(-15.0f + 4.0f * i, 28.0f);
box_[i] = world_->createBody(config);

fixture.friction = friction[i];
box_[i]->createFixture(fixture);
}
}

namespace {

float dist(const Vector2& u, const Vector2& v) {
return Vector2(u.x - v.x, u.y - v.y).length();
}

} // namespace

TEST_F(VaryingFrictionTest, AfterOneMinute) {
const int steps = 60 * 60; // One minute
const float time_step = 1.0f/60.0f;
for (int i = 0; i < steps; ++i) {
world_->simulate(time_step);
}

// Box #0 and #1 are on the top track.
for (int i = 0; i < 2; ++i) {
EXPECT_LT(dist(box_[i]->position(), track_[0]->position()),
dist(box_[i]->position(), track_[1]->position()));
EXPECT_LT(dist(box_[i]->position(), track_[0]->position()),
dist(box_[i]->position(), track_[2]->position()));
}

// Box #2 is on the bottom track
EXPECT_LT(dist(box_[2]->position(), track_[2]->position()),
dist(box_[2]->position(), track_[1]->position()));
EXPECT_LT(dist(box_[2]->position(), track_[2]->position()),
dist(box_[2]->position(), track_[0]->position()));

// Box #3 is on the ground
EXPECT_FLOAT_EQ(0.51503909f, box_[3]->position().y);

// Box #4 is out of the screen
EXPECT_TRUE(box_[4]->position().length() > 2000.0f);
}

} // namespace box2d
} // namespace physics_simulation
強力推薦大家使用 Google C++ Testing Framework. 接下來再寫幾個 acceptance tests,然後開始偷竊游戲物理設定。

2011年2月12日 星期六

Tendenz = Geld + Psychologie

趨勢等於資金加心理。現在來分析心理。先看幾個消息:

摩根富林明調查:台灣投資人信心指數大躍進 (2011.02.09),就業市場,進入求才年 (2011.02.09),消費者信心指數新高 景氣復甦有感覺了 (2011.1.26)。

群眾心理不言可喻。

不過事情總是有變化,群眾心理有點鬆動,繼續聽市場的聲音吧。

交易員的靈魂壹之一


Q: 2006 年 12 月 12 日出現一根跌幅超過 2%以上的長黑日K線,不妨就從這個提示開始思索:過去幾次遇到上述情況後的大盤表現,以往回檔過程中總共出現幾次跌幅超過 2%的長黑日K線?

A: 不知道。

Q': 某月出現一根超長的黑月K線,過去幾次遇到上述情況後的大盤表現,以往回檔過程中總共出現幾次超長的黑K月線?

A': 送分題自己查。這次呢?先等二月份過完吧。

另外,自己也清楚 Zurich axiom 所說的:On Patterns - Chaos is not dangerous until it starts to look orderly. Beware the chartist’s illusion - it is characteristic of human minds to perceive links of cause and effect where none exist. 就算是送分題,也要特別小心,免錢的最貴,只有思考才是王道。未來會怎樣?老話一句:「不知道」

2011年2月11日 星期五

RE: 美元匯率趨勢

彭神指出,新台幣在是今年全球升值幅度最大的貨幣,但新台幣大幅升值,會讓企業出現匯損。彭淮南說,最近還收到做雨刷企業主電子郵件抱怨,說新台幣升值,但國外買主不同意調價,已一、二個月拿不到訂單了。

彭神表示,新台幣兌美元匯率升值可使新台幣計價的進口物價升幅,低於以美元表示的物價升值幅度,舒緩進口物價上漲壓力,不過台幣也不能一直升,因為從總體來看,出口降低,人均所得也會降低。

彭神指出,很多人誤以為新台幣升值會讓購買力增加,但事實上從國民總所得來看,如果台幣升過頭,造成出口減少、貿易出超下降,以新台幣表示的國內生產毛額(GDP)會下降,民眾所得反而減少,根本沒錢去買東西。

彭神說,央行會持續監督外資動向,要求資金匯入後最多只能留下3%當周轉金,其餘要在一周內投入股市;未來外資每匯入10元,最多只能拿3元去買公債等固定收益商品,剩下7元都必須投入股市,否則就得立即撤出。

彭神表示,據央行掌握資訊,先前沒有投入股市的疑似熱錢兵分三路,分別停泊在借券保證金、公債和新台幣存款。



外資還會炒匯嗎?這是買點還是埋點?結論跟上篇類似:「未來向上還是向下?我不知道,真的不知道,但彭神的確很關心台幣。」

這次不一樣

Tendenz = Geld + Psychologie,趨勢等於資金加心理。

資金方面,昨天彭淮南指出,外資慣用的操作手法就是,波段買進、波段賣出,之前已經累計匯入三千多億,也在期貨大量放空,對外亂喊新台幣會升到多少多少元,卻適時賣出、獲利了結「大賺兩方面」,但「股市的部分我不評論」。彭淮南僅表示,外資持有台股市值約三成,一旦資金撤出,有相當大的影響力。至於目前是否為外資由買轉賣的波段起始點,彭淮南仍保持一貫神祕地表示,「匯價由市場決定,央行只是維持動態穩定」。

(註:大資金「波段買進、波段賣出」十分合理。至於「對外亂喊新台幣會升到多少多少元」,怎麼看都不像「亂喊」,這僅僅是完美投機計畫步驟之一,被騙只是剛剛好。)



當然,上面消息只代表「三成」股市的聲音,其他七成怎麼說,我不知道。至於心理方面,不予評論。

這次不一樣?真的嗎?其實我很害怕,金融海嘯前,各類基金炒翻天,投資型保單狂賣。現在房地產也夯到不行,最近也有三個單位向我推銷儲蓄型保單,更別說有人拉我直銷。

(某層面被說服了,個人需要「教育訓練」素材。至於拉下線賺獎金,這我不幹。錢不是臭的,但拉下線的錢是臭的。)

(我朋友認為:「先有很多錢,才創業」,這觀念不對,應該是「先創業,錢會自己過來」。至於「複製」,除非有改變,每個人都在複製舊有的習慣,要改變才會有機會。至於拉下線就不用說了,你還是為某人工作,拿微乎其微的獎金。月入百萬?哪個行業最頂尖的人月入不到百萬?就算是最噱的黑道,下面小弟有月入百萬嗎?公司規模 892m usd,敢唬爛我 400b usd,這也差太多了吧。)



個人相信:這次還是一樣。歷史就是不斷的重演。

2011年2月10日 星期四

美元匯率趨勢


Link: ForecastChart.com

點選 20 Year Graph - Taiwan Dollar (TWD) 可看到 20年長期趨勢,截圖在旁邊。未來向上還是向下?我不知道,真的不知道,但台幣的確在升值。

ForecastChart.com 網頁旁邊有 Currency Exchange Rate Forecasts,可以觀察幾個重要的通貨,研究一下趨勢,有感覺了嗎?

這就是趨勢。

註:最近美金儲蓄險很夯,友人一直向我推薦。個人認為,凡事都要「簡單化」,如果看準美元已經升不上去,準備要狂貶了,直接買美元才是最簡單,也是最好的投機方式。美金儲蓄險「不是」簡單的投機方式。

此外,Max Gunther 說:Long-range plans engender the dangerous belief that the future is under control. It is important never to take your own long-range plans, or other people’s, too seriously. 說的太好了,六年之後的事,不是我們能操縱的,把「美金儲蓄險」當做「美金儲蓄險」,問題會簡單的多。