気まぐれな戯れ言 バックナンバー4


気まぐれな戯れ言バックナンバーです。全体の一覧はこちら

01/10/10 オセロ思考編
01/07/10 どんどん新天地編
01/04/25 さらに新天地編
01/01/08 初めての自作パソコン編
01/01/06 FPUでSin、Cos編
00/11/12 テクスチャ付3角形−基本編
00/10/30 数式解析編
00/10/05 アップグレード編

01/10/10 オセロ思考編
最近家でホームページ向けプログラム書いてないです・・・
さすがに授業やレポートでプログラム関連が多いと疲れるんでしょうかね。

で、オセロ。
PROgramming LOGic略してPrologなる言語を「論理型言語」の代表として1学期間(の半分)授業でやってきたんですが・・・
最後の課題が「オセロの思考ルーチン」だった。

対戦はサーバープログラムとクライアントプログラム2つで、ソケット通信でやるというもの。
ただ、サーバープログラムや、通信の部分のプログラム、盤上の色をひっくり返す処理などは用意されている。
要は思考ルーチンだけ作ればいいと。

最低TA(Teaching Assistant、この授業では修士の人)の人のサンプルプログラムの勝てるもの(4回中2回以上勝てばよい)を作成すると言うことらしい。
ちなみに、そのサンプルはあんまり強くなくて、「各マスに優先順位をつけ、置けるマスの中で最も優先順位の高いところに置く」だけ。
ただ、これが(素人とやると)意外と強い。

んで、自分のプラン。
定石は入力がめんどくさいし、かなりファジーな判定をして利用しないと上手く使えないので今回はやらない。
前半・中盤は「各マスにポイントをつけ、N手後の盤の各マスの合計値からmin-max法で選出」。
終盤は「全解検索を行い、終了時の石の数の差からmin-max法で選出」。

ちなみに、最終的にはN=4、52手目から全解検索なんだけど・・・

min-max法は詳細はゲーム理論の本などを見てもらうとして、オセロは「完全情報ゼロ和ゲーム(お互いどこに打てばどういう状況になるかわかり(=完全情報)、互いの利害は完全に対立(=ゼロ和))」なので、基本的に「敵は最悪の手を打ってくる。最悪の手を打たれた場合一番マシになる場所に置く」と言うことを再帰的に行います。
敵は最悪の手(min)を取ろうとするので、その中で最も大きい物(max)を取るわけ。

で、ここで重要なのが「各マスにポイントをつけ」と言うところ。
この「ポイントのつけ方」を評価関数などと言いますが、これの良し悪しでかなりの部分が決まります。
もちろん「角は非常にポイントが高い」「その隣は逆にポイントが非常に低い」などです。
実際は「角を自分が取っているならその隣に置いても良い」など、もっといろいろな判断を行いたいところでしたが、やはりPrologでは配列処理などは少しやりづらいこともあり、今回は止めました。

んで、簡単に実装してみる。
間に「自分が置いた後敵が置けないのに、敵の手を読もうとしてエラー」とか、「カットの使い方が間違ってるよ」など、Prologならでは(前者はPrologでやったら「述語なし」になり無限ループ)のエラーにも苦労しましたが・・・

うーん、弱い。
TAのサンプルに歯が立たん。
この時点ではN=2、53手目から全解検索なんだけど・・・
ちょこちょこ評価関数の値をいじってもダメ。
良く見ると中盤までは結構取ってるのに、終盤一気に取られる。
つーか明らかに角も取れて10個ぐらい取れる手を捨てて1個しか取れない手を打ってる・・・
・・・全解検索でminとmaxが逆になってた。

とりあえず完成。
せっかくなので、DLLによるプラグイン形式で思考ルーチンを追加できるフリーウェアのオセロがあったのでC言語でも書いてみる。
速度的にもだいぶ早くて5手先読み、50手目から全解検索ができるようになった。

んで、この時点でProlog版はN=3。
これだと1ゲームで5分ぐらい考える。
N=4にすると20分以上すると思われる。
このオセロ課題は「思考時間は合計30分以下」なので、N=4は時間的に不安。

んで、α−β法を利用することにした。
これは探索木を大幅に少なくする方法で、明らかに不必要な検索を行わないことで高速化できる。
詳細は同じくゲーム理論の本で見てください。
思考ルーチンがらみの本でも多分載ってます。
これだとさらにC版では2手、Prolog版では1手深く読んで速度的に大体同じになった。


ちなみに、学科の人間でトーナメントを開いたところ、2回戦で敗退・・・
パーミッションの関係でソースは見られなかったものの、TAのディレクトリに他の人のソースも置いてあるのだが、自分のコードの10KBに比べ、20〜30KBの長さのソースの人も少しいた。
やはり定石を使っているのだろうか・・・?

上の学年の中では学習機能をつけて、プロの対戦の棋譜をたくさん覚えさせてやたら強くなったプログラムがあるらしい・・・


実際、オセロは角を取る以外にも、「中盤は石を多く取ることより、自分が多くの手を打てることを考える」「相手の中に自分の石が入る形にする」など、重要な要素もあります。
当然隣に石があるかないかによって評価関数も変える必要がありますね。
そう言うことを色々やれば結構強くなるんでしょう。(^^;)


ちなみに、下のURLに行くと最強と言われる(↓つーか言ってるな)オセロと対戦できます。
Javaアプレットのようです。
通信でやってるのか思考ルーチンもダウンロードしてるのか良くわからないですが・・・
”最強”コンピュータオセロ「ロジステロ」と対戦!

01/07/10 どんどん新天地編
最近忙しい・・・
専門に入ってレポートが多いこともありますが、部活の試合等ではパソコンを使う仕事がよく回ってきます。
まぁ部活のHP運営をしてることもありますが。
おかげでプリンタでの最低限の印刷が出来るようになったし・・・(で、リーグ戦印刷・トーナメント表印刷ができた)。

で、かなりレポートが多いです。
現時点での残り。
締め切り−明後日−OS演習(複数プロセス実装)
7/31−Scheme(QM法論理圧縮、4-Adder速度比較)、VHDL(ALU、ステートマシン)
8/3−頼まれたレポート手伝い*2(^^;)
8/31−VHDL(multiplier、プライオリティーエンコーダー)、回路作成(JKラッチ)、OS演習(TLB実装)
9/20−OS演習(TLB実装)
あとは任意課題が2つ。

ちなみに、7/25〜8/18の間で部活の試合&合宿連発で3日以上連続で休める日が無いんだけど・・・
いつやれと・・・?

・・・。
書くことが無くなってしまった。
最近忙しくて趣味のプログラムをあんまり書いてないんだよね・・・
8月を耐えきれば・・・?

現在OS演習の課題に奮闘中。
自前で簡易OS作成(正確には拡張)なる課題なんですが・・・
複数プロセスを同時に動かそうとするとメモリのページ処理をしないと行けない。
めんどくさすぎ・・・
文字列を読み取るときもページの境またいだら切れないように処理しないと行けないし。
OSって結構すごいことやってるのね。

01/04/25 さらに新天地編
ついに専門課程の授業が始まりました。

幸いにしてうちの学科は授業数が少ないけど、レポートがかなり多い・・・

週3回「実験」の名のつく授業があるけど、実際はレポート課題の説明をやって終わり。
ただ、言語がどうもなじめない・・・

月曜はどうも論理回路に関する実験らしく、使う言語は前半Scheme、後半VHDL。
Schemeは前学期も使ったLisp系言語だけど・・・
VHDL?
どうやら論理回路記述用の言語らしい(?)
んで、現在Scheme課題が3つまで出てきてるけど・・・
2回目にして「論理式を与えられたらその真理値表を返せ」だと。

もしや式のパーサーを作るんですかぁ(T-T)という状況ではあったが、(実際前学期Scheme演習は最後Schemeのインタープリタを作るという課題だったけど・・・まぁある程度完成されているインタープリタに昨日を追加すると言うだけなんだけど)幸いSchemeにはeval関数があるので、そこは必要なし。
ただ、問題は「論理式中の変数名の一覧を(重複なく)どうやって得るか」なんだけど・・・
結局パーサみたいなことしないと行けないじゃん。
しかも重複を許さないリスト追加関数とかも作らなきゃ行けないし。
その他もろもろ面倒なことが。
第3回目はもうちょいハードなんだけど。。。
まぁ数式解析に当たるのかな。
論理式の真理値表も似たようなもんだけど。

火曜は言語コースとか書いてあったな。
前半はOCamlなる言語。
Scheme同様に関数型の言語だね。
今までレポートが3回分出てるけど今日2回分終わらせた。
こっちはまだ2分木操作とかの、再帰関数をうまく使ったリスト等の操作だからラクかもね。
まぁ3回目にもなると結構厳しそうだけど。

木曜はOS演習。
これはC++とかだからまぁ安心できるかな。(?)
まだ1回しかやってないけど、とりあえずcpコマンドを作れとかそんな感じ。
後半は簡単なシェルを作るとか仮想ハードウェア用の簡単なOSを作るらしい。
シェルはともかく、OS自体を作るってどうやるんだか想像もつかないね。


しかし今までと違って端末室の環境もいいね。
今までのHappy Hacking Keyboardは個人的には使いづらかったし・・・

しかしサーバーの状態を見るとすごいねぇ。
普通にメモリ8192MBとかスワップ用領域2GB(メモリより少ない?)とか出てくるし。
そもそも/homeディレクトリ用に210GBの領域が用意してあるとか。
つい最近入れ替えたんだけどね。

まぁVBはVBでサボらないようにしないとね。
あと1週間でMicro War開発4周年(^^;)

01/01/08 初めての自作パソコン編
今までうちのパソコンはグラフィックが弱かったため、年末に最近流行りの(?)廉価なGeForce2MX搭載のグラフィックボードを買いました。
しかし、うちのAptivaにはAGPバスがなかった・・・

一応これまでのグラフィックボードはマザーボードにオンボード(ややこしい・・・)のAGP Rage Pro 2Xというやつで、さらにマザーボードには明らかにPCIではないところがあったからこれはAGPだろうと思ったら・・・
ISAでした(^^;)

んで、実家に帰った際、実家のAptiva J31もいいかげん古いかなということで、自作をして現在のAptiva243を実家に送るという計画を立てました。
なお、古いAptivaJ31もFDDとCDドライブはまだ使えるので(実家から帰り際に持ってきました。)

免許を実家の方で取って(取れて良かった(T-T))帰りの電車で、適当に買った「Athlon 1GHzマシン自作」が特集になってた雑誌を読む。
狙うはAthlon 1GHz!
早起き+電車で疲れたので1日置いて、¥45000持って秋葉原へ。

小田急線で下北沢まで定期を持っていて、井の頭線で渋谷まで持っているため、小田急で新宿魔で行くと(下北沢−新宿)+(新宿−秋葉原)=¥310だが、渋谷を回ると(渋谷−秋葉原)=¥190。
んで、渋谷経由。(いつもそう)
もっとも今回に関しては帰りは普通に新宿経由で帰る予定ですが。
っていうか、どうせ中央線快速に乗るために渋谷から新宿まで移動するんだよね。

取り合えず、何店か回る。
いつも行くSofmap、T-Zone、ツクモの他、今回はwww.kakaku.comでCPU・マザーボードが安かったPC-Succcess、Faithにも行くことに。
とりあえず2周ほど回る。

とりあえず店ごとに値段の差がないもの、ジョイパッド(実家に送る用)、ケース用ファン、CPUファン、シリコングリスを購入。
ちなみにファンの値段が思ったより高く、(っていうかケースはファン添え付けだと思ってた・・・)かなりヤバイです。
ジョイパッド−¥680(ゲームポート。8ボタンだがボタン配置が変)
ケース用ファン−¥1500
CPUファン−¥2300(なんかAthlon1.3GHzまで対応してるらしい・・・)
シリコングリス−¥380(なんか検便(食事中の方すいません)の容器みたいなやつにちょっと入ってるやつ。雑誌では¥1300前後のPent4用の注射器型を薦めてたけど。)

さて、関連部品は買ったところで残るはCPU、マザーボード、ケース。
ケースは重いので最後に買いたいところだが・・・
とりあえずSofmapは値段的にいずれも他より¥1000程高く、論外。
マザーボードはGIGABYTEのGA-7ZXが組みやすく初心者向けと聞いたので探すが、大体¥15000〜¥16000。
マザーボードは¥12000程度の予定だったが、単にkakaku.comでGA-7ZMと勘違いしてただけだった・・・
唯一¥15000を切る値段で(消費税入れれば超えるが)売ってたのがFaith。
とりあえず購入。
PC-Successはkakaku.comで非常に安かっただけに期待していたが、GA-7ZXは¥15000強、CPUはほぼ売りきれ。
つーか遠い。
大通り1つ向こう側じゃん。

んで、ケース+CPU探し。
最近はAthlon 1GHz前後は人気があるらしく、900〜1100MHzは店によってあったりなかったり。
1GHzで最安値\21600だった。
なお、950MHzは\18600〜\21000、900Hzは\16000〜\18000程度だった。

ケースもとにかくATXで300Wな物なら\6000もあれば手に入るのだが・・・
Athlonは電源との相性があるとの事でAMD推奨電源にする。
た、高い・・・
「普通だと\7000だけどAthlon対応電源にすると\11000」とかそんなんが多い・・
とりあえず、新しく出来たツクモ(黒いやつ)でAOpenのHT45Aを購入。
ぬ。
あのー、箱に「9.13KGS」とか書いてあるんですが・・・(^^;)
9キログラム?
これまで一体型、デスクトップ、ミニタワーと来ていたので、ミドルタワーの9キロですら最大級の重さだった。

とりあえず重いケースを持ちながら・・・
ケース・マザーボードで完全に足が出たのでCPUは950MHzで妥協することに(もちろん5%ぐらい後でクロックアップするつもり( ̄ー ̄))
んで、950MHzが一番安かったのがツクモパーツ館。
他のツクモでも値段はほぼ同じと考えられるがSOLD OUTだった。

んで、950MHz購入。
マザーボード−GIGABYTE GA-7ZX \15000。
ケース−AOpen HT45A \8500。
CPU−AMD Athlon SocketA Bulk 950MHz \18600。

はぁ、重い・・・
すでにケースを持ってかなり移動してる。
っていうかなぜか初雪がちらほら。
まぁT-Zone→ツクモパーツ館→秋葉原駅で数百メートルだけど・・・
切符を先に買っておいてよかった。

とりあえず自分の駅に着いたときは幸い雨も雪も降ってなかった。
(夜に降ったが・・・)

とりあえず、自作開始!
まずはCPUを取り出してソケットにつける。
これは以前K/6-2 350MHz→500MHzの時にもやったし楽勝。
んで、シリコングリスを塗る。
量がわからないんで適当に・・・

んで、ヒートシンク+ファンをつけるのだが・・・
ヒートシンクを固定する金具が硬い・・・
ぐおおお。
力を入れてもつかない。金具が曲がらない・・・(わかる人だけわかってくれ)
爪が痛い・・・
しょうがないんで分解して(ってファンを取るだけだが)金具を取りだし、電熱器(うちはガスキッチンじゃないのだ)に当てつつグリグリ曲げた。
んで、再装着!(っていうか一度もついてなかったが・・)

ちなみにこの金具との格闘はサザエさんをみながら1時間にも及びました。
「早くAthlonの実力を見たい」と思いつつの作業・・・気はあせる。

んで、マザーボードをケースに固定。
あとはドライブ・メモリの装着なので楽勝。
メモリをAptivaから128MB取ってつけ、HDDもとりあえず15GBのやつをつける(注:OSとかデータが入りっぱなし。無謀としかいいようがないのか?)
以前買ったグラフィックボードをつけ、LEDランプの線もつないだ。
ちなみにLEDランプの配線は雑誌のおかげでわかりやすかった。
これがなかったらつけてなかったかもしれない・・・マニュアル英語だし。

んで・・・起動!
ぐおおおん、ババババババ!
なんかすごい音がするなぁ・・・
おお、起動画面に「950MHz」の文字が!
感動したまま起動。
何もなくあっさり動く。
さすがにWindows起動時にわけのわからないドライバがいっぱいインストールされるが・・・
しかし、その後「NO ENOUGH SPACE FOR ENVIRONMENT」だったかそんなようなエラーで起動せず。
Safeモードとの行き来を繰り返してサウンドがらみの問題と判明。
サウンドドライバをはずしてCD、FDDを装着。 「VIA 4 in 1」とかいう良くわからないが入れないとまずいものをインストール。
サウンドドライバも入れて・・・

ここでフロッピーを入れようとして気づいた。
FDDの形が変だ。
以前のAptiva用になっているため、前面が多少形が変わっていてうまくつかえない。
しょうがなくFDDは実家から持ってきたものを使う。
ちなみにこれはこれまで使っていたAptiva243の前に実家で使っていたAptivaJ31の前に使っていたPS/V Vision(CPU 33MHz、HDD 212MB、MEM 8MB)のものである。

んで完成!
メガデモがスイスイ動く。
今までOpenGL物はほとんど速度的な面で見られなかったので一気に見れるものが増えました。
VRAMが増えたのも大きいです。
残念ながら、オンボードのCT5880は音量が小さい・・・
あとAstralのAPOCもいいです。BIO HAZARDしてます。が、なぜか終了時フリーズ・・・

その後、やはり漢(^^;)なら1GHzを目指そうということでいきなりクロックアップ!
GA-7ZXは倍率は変えることが出来ないので(CPUに対して固定)FSBを100から106に。
よっしゃ、1007MHz!
やはりHDBENCHでも今までの500Mzより計算速度2倍!

ただ、メガデモ3連発とか見ると熱暴走起こします(70度近い・・・)

その後、あまりに音の小さいCT5880をあきらめ、\2000の安っちいサウンドカードを買いましたが、とりあえず満足。
しかし夜中寝ながらDLするときは、ケースのファンがうるさいので切ってます。

参考までに、HDBENCHの値を。
 ★ ★ ★  HDBENCH Ver 3.22  (C)EP82改/かず ★ ★ ★ 
M/B Name      
Processor   AMD Athlon 1006.44MHz[AuthenticAMD family 6 model 4 step 2]  
Cache       L1_Data:[64K]  L1_Instruction:[64K]  L2:[256K]
Name String AMD Athlon(tm) Processor
VideoCard   WinFast GeForce2 MX Display Adapter  
Resolution  800x600 (16Bit color)  
Memory      130,028 KByte  
OS          Windows 98 4.10 (Build: 1998)    
Date        2001/01/12  22:14  

HDC = VIA Bus Master PCI IDE Controller
HDC = Primary IDE controller (dual fifo)
HDC = Secondary IDE controller (dual fifo)

A = GENERIC NEC  FLOPPY DISK    
C = GENERIC IDE  DISK TYPE47    
DE = GENERIC IDE  DISK TYPE47    
F = SONY CD-ROM CDU701-F  Rev 1.0q

   ALL  Integer   Float  MemoryR MemoryW MemoryRW  DirectDraw
 21198    40726   49479    15606   19447    24663          59

Rectangle   Text Ellipse  BitBlt    Read   Write    Copy  Drive
    37792  19980    5413     997   18927   17885   22346  C:\20MB

しかしサウンドボードをつけたころから異常に熱が高くなるようになったような・・・?
グリスしっかりつけるか?

01/01/06 FPUでSin、Cos編[★★★◎◎]
新年明けましておめでとうございます。
ついに21世紀、どうもやる気の無い自分に喝をいれつつもっとがんばっていきたいと思います。


最近DrawToolsで回転や半透明の要望を受けることが多くなった。
半透明は以前からもDIBSectionを利用してCやアセンブラで組んできたけど、回転は本格的には試したことが無かった。

普通の半透明は描画先、描画元の画像データの並び方が(ピッチを除けば)同じだったから、MMXなどを利用して高速化することもできたんだけど、回転はそうも行かない。
なぜなら、描画元の座標がX座標で1ずれたら、描画先の座標ではX座標がCosθずれて、Y座標はSinθずれなければいけないから。
このせいでどうしても速度的には遅くならざるを得ない。

かのWinGLとかを見るとかなり速いけど、やはりグラフィックボードの3D機能を用いてD3Dでコーディングした方がはるかに速いし、CPUにかかる負担は全然違うと思う。
ってなわけで以前「DrawToolsで回転機能をつけてよ」といわれたときに速度的には妥協して、とりあえず動くものを作った。
目的としては、画像のロード時に回転した画像をあらかじめ作成してしまおうというもの。
(いくら遅いといっても、うちのマシン(K/6-2 500MHz)で8Bitのサイズ32*32のデータを100個描画したところ、30FPSは出たので(透明色の処理などはしてないけど・・・)1秒程度あればまぁ大概終了するでしょうということで。
あらかじめ回転パターンをBMPに作成しておくというパターンもあるけどね。

んで、問題なのが、この回転を実装するためにmath.hをインクルードしたところ、sinとcosしか利用していないのにDLLのサイズが38KBから70KBと約2倍近くなってしまった。
できればDLLのサイズなんて小さいに越したこと無いし・・・

とりあえず、C言語そのものにはあまり詳しくないので、たとえMath.hの中の関数で利用していないのにDLLに入っていたとしても取り除き方がわからない・・・
しかもCランタイムのソースをみても肝心の関数のプロトタイプが書いてあるだけで、実際の中身はLIBファイルになっているらしく、わからん。

んで、今までやったことの無かったFPUの利用にチャレンジ。
とりあえずFPUは使い方が良くわからなかったので・・・

とりあえずマニュアルを見る。
最初にFINITをしておくといいみたい。
どうもスタックで計算するらしい。

・・・。
良くわからん。
まぁいい、試してみよう!
ということで、次のコードを組んでみる。

#include <iostream.h>

double __DTSin(double);
double __DTCos(double);

//FPU利用のsin
double __DTSin(double deg){
	double d;
	__asm{
		FINIT
		FLD deg
		FSIN
		FST d
	}
	return d;

}

//FPU利用のcos
double __DTCos(double deg){
	double d;
	__asm{
		FINIT
		FLD deg
		FCOS
		FST d
	}
	return d;

}

void main(){
	int i;
	double d,ds,dc;
	for(i=0;i<=360;i+=5){
		d=i*3.1415926535/180;
		ds=__DTSin(d);
		dc=__DTCos(d);
		cout << "sin" << i << "=" << ds;
		cout << "\tcos" << i << "=" << dc << endl;
	} 
	return;
}

printfなんかを使うと面倒なんで、ほとんど使ったことのないiostream.hを使ってみました。
なぜかコンパイルできないと思ったら拡張子がcppでなくcになっていたというくだらないミスをしたりもしましたが。

FLDやFSTの引数は変数名でいいのか等が全然自身無いながらも実行!

んで、出力結果(ちなみに入力は360度系の角度ね。ラジアンではない。っていうか思いっきりforループ内で変換してるし。
sin0=0              cos0=1
sin5=0.0871557      cos5=0.996195
sin10=0.173648      cos10=0.984808
sin15=0.258819      cos15=0.965926
sin20=0.34202       cos20=0.939693
sin25=0.422618      cos25=0.906308
sin30=0.5           cos30=0.866025
sin35=0.573576      cos35=0.819152
sin40=0.642788      cos40=0.766044
sin45=0.707107      cos45=0.707107
sin50=0.766044      cos50=0.642788
sin55=0.819152      cos55=0.573576
sin60=0.866025      cos60=0.5
sin65=0.906308      cos65=0.422618
sin70=0.939693      cos70=0.34202
sin75=0.965926      cos75=0.258819
sin80=0.984808      cos80=0.173648
sin85=0.996195      cos85=0.0871557
sin90=1             cos90=4.48966e-011
sin95=0.996195      cos95=-0.0871557
sin100=0.984808     cos100=-0.173648
sin105=0.965926     cos105=-0.258819
sin110=0.939693     cos110=-0.34202
sin115=0.906308     cos115=-0.422618
sin120=0.866025     cos120=-0.5
sin125=0.819152     cos125=-0.573576
sin130=0.766044     cos130=-0.642788
sin135=0.707107     cos135=-0.707107
sin140=0.642788     cos140=-0.766044
sin145=0.573576     cos145=-0.819152
sin150=0.5          cos150=-0.866025
sin155=0.422618     cos155=-0.906308
sin160=0.34202      cos160=-0.939693
sin165=0.258819     cos165=-0.965926
sin170=0.173648     cos170=-0.984808
sin175=0.0871557    cos175=-0.996195
sin180=8.97932e-011 cos180=-1
sin185=-0.0871557   cos185=-0.996195
sin190=-0.173648    cos190=-0.984808
sin195=-0.258819    cos195=-0.965926
sin200=-0.34202     cos200=-0.939693
sin205=-0.422618    cos205=-0.906308
sin210=-0.5         cos210=-0.866025
sin215=-0.573576    cos215=-0.819152
sin220=-0.642788    cos220=-0.766044
sin225=-0.707107    cos225=-0.707107
sin230=-0.766044    cos230=-0.642788
sin235=-0.819152    cos235=-0.573576
sin240=-0.866025    cos240=-0.5
sin245=-0.906308    cos245=-0.422618
sin250=-0.939693    cos250=-0.34202
sin255=-0.965926    cos255=-0.258819
sin260=-0.984808    cos260=-0.173648
sin265=-0.996195    cos265=-0.0871557
sin270=-1           cos270=-1.3469e-010
sin275=-0.996195    cos275=0.0871557
sin280=-0.984808    cos280=0.173648
sin285=-0.965926    cos285=0.258819
sin290=-0.939693    cos290=0.34202
sin295=-0.906308    cos295=0.422618
sin300=-0.866025    cos300=0.5
sin305=-0.819152    cos305=0.573576
sin310=-0.766044    cos310=0.642788
sin315=-0.707107    cos315=0.707107
sin320=-0.642788    cos320=0.766044
sin325=-0.573576    cos325=0.819152
sin330=-0.5         cos330=0.866025
sin335=-0.422618    cos335=0.906308
sin340=-0.34202     cos340=0.939693
sin345=-0.258819    cos345=0.965926
sin350=-0.173648    cos350=0.984808
sin355=-0.0871557   cos355=0.996195
sin360=-1.79586e-010 cos360=1

あのー、いきなり成功しちゃってるんですが(^^;)
sin360が0になってないなど、微妙な違いはあるものの、ここらへんはどうせ角度とラジアンの変換の性だし、はっきり言って問題無し!
Win95以上であれば486DX2以上だし、(多分)FPUもついてるから、いきなり実装!

んで、サイズは・・・65KB?
あれ?回転ルーチン自体はそこまで大きくないはず。
ためしに回転ルーチンおよびSin、Cosを除いてみる。
38KB。
今まではこのサイズだったよなぁ・・・
Sin、Cosをいれてみる。
む、62KB?
Math.hをつかってなくてもFPU使うだけでここまで大きくなるの?
なんで?

逆に、回転ルーチンからSin、Cosを除いてみる。
ん?64KB?

調べてみたところ、double型の計算が1度でも入ると遅くなるみたい。
回転ルーチンには、「double型に0x10000をかけて固定小数点化したintを作成する」という処理があるんだよね。
一番の回避策は・・・VB側でsin、cosの結果を計算して送ることだけど・・・面倒だね。
「最初から0x10000かけた状態のSinを作成する」ってのもあるけど・・・
そもそも引数がdoubleな時点で終わってる。
あとは・・・テーブル作成しか。
角度360パターン*SinとCosの2パターン*intは4バイト=2880バイト・・・でもいいけど。

サイズが増えるけど我慢してください。。。
テーブルの方が良ければ対応します。

とりあえずmath.hからFPUにしたら70KBが65KBになったけど・・・なんか納得行かず。

さて、透明色の実装もしないとな。
っていうかこれ、8Bit専用になってるし。

っていうか、よく見たらDLLに渡す角度って360度系になってるし・・・
テーブル実装決定!(^^;)

普通256とかなんだろうけど、わかりやすさを考慮してあえて360。
回転ルーチンも書きかえる。
・・・。
47KBに落ち着きました。

まぁ今回はFPUの基礎が学べただけでもいいとするか。

00/11/12 テクスチャ付3角形−基本編[★★◎◎◎]
さて、いいかげんに3Dにも手を出さないとね。
しかし指を切って左人差し指にバンドエイドをつけてるんだけど・・・
かなり先の方につけてるため、端が指からはみ出ててキーボードが非常に打ちづらいです。

んで、ついに手を出す簡易テクスチャ付3角形描画。
(正確にはVBでもやったことがあるけど、スキャンラインを意識せずやったりして速度的に問題ありだったし)
今回は8bitでアンチエイリアスとかライティングとかしないことに。
しかも3Dではないんでパース補正もやってない。
本来はテクスチャ抜きでとりあえず単色三角形を描画する方がいいかもしれないけど、1年後には実験でレイトレーシングするって言うんで。(なんだそりゃ)

とりあえずいくつかのコードを見た感じ次のようにやるみたい。

頂点p[0]、p[1]、p[2]はメンバとしてx,y,u,vをintでもつ。
まず3頂点をyでソート。
んで、p[0]、p[1]、p[2]のx,y,u,vからx,yそれぞれ1ピクセルあたりのu,v変化量を計算。
(なお、浮動小数点を利用しないように0x10000を掛けることで整数演算にする)
んで、p[0].yからp[1].yまで、とりあえず描画、p[1].yからp[2].yまで描画(←なんつー適当な書き方だ)

途中で「VBと違ってCはキャストしないと整数/整数は小数にならない」ってのを忘れてて描画をし忘れたり、p[0].y=p[1].yの時に飛んだりして一苦労・・・
なんか時々1・2ピクセルぐらいテクスチャがずれるんだけど・・・
いくらなんでも0x10000倍してれば固定小数点でもそれほど誤差は出ないだろうに・・・
ただ三角形を(2D的に)回転させてるだけなのに。

んで、320*240のウインドウにDIBSectionを使って描画。
256*256の正方形を2分割した三角形をそれぞれ回転させる。
うーむ、Cで書いたままとはいえ65FPSとは・・・
もっとも、三角形描画だけなら100FPS超えてるんで、どうも裏バッファのクリアやウインドウへのBitBltが時間を取ってるっぽい。
ここらへんはDDrawなら改善できるだろうしね。
しかしそれでも256*256分だけでこれは遅い。
何が遅いかはこれからチェックします。
目標は「640*480を60FPS!」(をい)
いや、これなら320*240では60FPSで画面4回分更新できるんで、かなり複雑にポリゴンポリポリ(なんだそりゃ)できるじゃん。

とりあえずコード。
DIBSecはDIBSectionを持つ構造体です。
プログラム実験部屋にあるVBコードのDIBSecとBYTE配列の有無を除き同一です。
要は高さ・幅・ラインアライメント・HDC・画像の先頭ポインタを持つと。

struct PolyPoint{
    int x,y,u,v;
};

inline byte GetVal(byte x,byte y){
//インライン関数、x,yからそのデータを得る。
    x &=127;
    y ^=255;
    y &=127;
    //y = 127-y;
    int ty=y;
    return *((byte*)TempDIB.hMem+(ty<<7)+x);
    //return *((byte*)TempDIB.hMem+128*(127-ty)+x);
}

int DrawTextureTri(DIBSec* DestDIB,DIBSec* SrcDIB,PolyPoint* p1,PolyPoint* p2,PolyPoint* p3){
//テクスチャ付き三角形描画
    PolyPoint pp[3],tp;
    int dudx,dudy,dvdx,dvdy,dxdy1,dxdy2;
    int i,j,k,l,x1,x2,tx1,tx2,y,u,v,ul,vl;
    int m11,m12,m21,m22,m1,m2;
    byte bu,bv;
    double td;
    byte* pByte;


    //Y座標順にx,y,u,vをセット
    pp[0]=*p1;pp[1]=*p2;pp[2]=*p3;
    if(pp[0].y > pp[1].y){ tp=pp[1];pp[1]=pp[0];pp[0]=tp;}
    if(pp[1].y > pp[2].y){ tp=pp[2];pp[2]=pp[1];pp[1]=tp;}
    if(pp[0].y > pp[1].y){ tp=pp[1];pp[1]=pp[0];pp[0]=tp;}
    if(pp[0].y==pp[1].y && pp[0].x>pp[1].x){ tp=pp[1];pp[1]=pp[0];pp[0]=tp;}
    if(pp[1].y==pp[2].y && pp[1].x>pp[2].x){ tp=pp[1];pp[1]=pp[2];pp[2]=tp;}
    
    //逆行列からu,vの変化量生成
    m11=(pp[1].x-pp[0].x);
    m12=(pp[1].y-pp[0].y);
    m21=(pp[2].x-pp[0].x);
    m22=(pp[2].y-pp[0].y);
    i=m11*m22-m12*m21;
    if(i == 0) return 1;
    if(m22<=0) return 2;

    /*
    (a b)(x)=(e)       (x)=(d -b)(e)
    (c d)(y)=(f) の解は(y)=(-c a)(f)/(ad-bc)*/

    td=(((double)m22)*(pp[1].u-pp[0].u)-m12*(pp[2].u-pp[0].u))/i;
    dudx=0x10000*td;
    td=(-((double)m21)*(pp[1].u-pp[0].u)+m11*(pp[2].u-pp[0].u))/i;
    dudy=0x10000*td;
    td=(((double)m22)*(pp[1].v-pp[0].v)-m12*(pp[2].v-pp[0].v))/i;
    dvdx=0x10000*td;
    td=(-((double)m21)*(pp[1].v-pp[0].v)+m11*(pp[2].v-pp[0].v))/i;
    dvdy=0x10000*td;

    //pp[1]が左側
    ul=pp[0].u*0x10000;
    vl=pp[0].v*0x10000;
    tx1=tx2=pp[0].x*0x10000;

    if(m12!=0){
        if(((float)m11)/m12 <= ((float)m21)/m22){
            dxdy1=(0x10000*m11)/m12;
            dxdy2=(0x10000*m21)/m22;
        }
        else
        {
            dxdy2=(0x10000*m11)/m12;
            dxdy1=(0x10000*m21)/m22;
        }
        for(y=pp[0].y;y>16;x2=tx2>>16;
            u=ul;v=vl;
            if(x1<0) {u-=dudx*x1;v-=dvdx*x1;x1=0;}
            if(x2>=DestDIB->Width) x2=DestDIB->Width;
            pByte=(byte*)(((int)(DestDIB->hMem))+DestDIB->LineLength*(DestDIB->Height-1-y)+x1);
            if(y>=0 && yHeight){
                for(i=x1;i<=x2;i++,u+=dudx,v+=dvdx,pByte++){
                    bu=u>>16;
                    bv=v>>16;
                    *pByte=GetVal(bu,bv);
                }
            }
            ul+=dudy+(dudx>>8)*(dxdy1>>8);
            vl+=dvdy+(dvdx>>8)*(dxdy1>>8);
            tx1+=dxdy1;
            tx2+=dxdy2;
        }
    }
    
    if(pp[1].y==pp[2].y) return 3;
    if(m12==0){
        if(pp[0].x==pp[1].x) return 4;
        dxdy1=0x10000*m21/m22;
        dxdy2=0x10000*(pp[1].x-pp[2].x)/(pp[1].y-pp[2].y);
        tx1=pp[0].x*0x10000;
        tx2=pp[1].x*0x10000;
    }
    else if(((float)m11)/m12 <= ((float)m21)/m22)    {
        dxdy1=0x10000*(pp[1].x-pp[2].x)/(pp[1].y-pp[2].y);
        dxdy2=0x10000*m21/m22;
    }
    else{
        dxdy1=0x10000*m21/m22;
        dxdy2=0x10000*(pp[1].x-pp[2].x)/(pp[1].y-pp[2].y);
    }

    for(y=pp[1].y;y>16;x2=tx2>>16;
        u=ul;v=vl;
        if(x1<0) {u-=dudx*x1;v-=dvdx*x1;x1=0;}
        if(x2>=DestDIB->Width) x2=DestDIB->Width;
        pByte=(byte*)(((int)(DestDIB->hMem))+DestDIB->LineLength*(DestDIB->Height-1-y)+x1);
            if(y>=0 && yHeight){
                for(i=x1;i<=x2;i++,u+=dudx,v+=dvdx,pByte++){
                    bu=u>>16;
                    bv=v>>16;
                    *pByte=GetVal(bu,bv);
                }
            }
        ul+=dudy+(dudx>>8)*(dxdy1>>8);
        vl+=dvdy+(dvdx>>8)*(dxdy1>>8);
        tx1+=dxdy1;
        tx2+=dxdy2;
    }
    return 0;

}

なんかGetValが悪いような気がしてきた・・・
っていうかDIBSectionに限らずBITMAPが上下逆っていうのが厄介だよね。
DDrawはその点大丈夫だけど。
ところでDDrawではVRAMへのメモリコピーは遅いけど、DIBSectionは遅くないのかねぇ。

とりあえずそのうち高速化します。

うーん、なんかアンチエイリアス付拡大をやりたいんだけどねぇ・・・
縦の拡大はMMXが使えるんだけど、横拡大ではうまく使い方が思いつかない・・・
普通に1ピクセルずつやるべきか?

00/10/30 数式解析編[★★★◎◎◎◎]
久々にちょっと高度っぽいプログラムの話。
前からやってみたかった数式解析です。

文字列をテキストボックスに入力したら計算してくれるって奴。
PerlとかJavaScriptならevalとかいう関数で簡単にできるんだけどさ、普通の言語ではそう言うの無いんだよね、コンパイラ系だし。(VBも最終的にコンパイルして生成するEXEを利用するわけだからコンパイラって言うのもあながち間違ってないしね)

んで、自分が今まで思っていた「やるならこうだな」って言うのは・・・
1−木の構造にする。
2−文字式のまま優先順位の高い順にひたすら計算。

1はかなりやりづらい。
なぜかというと、VBではポインタが使えないから。
Cではポインタが使えるからpointer==NULLとかそう言う条件分岐とかすればいいけどさぁ。
VBはできないことは無いけどとにかくやりづらい。

んで、2。
これはメモリとかに関しては不安は無いけど、速度的に厳しそう。
何度も文字列内を検索するしね。
最初はカッコを探して、次は*と/をさがして、次は+と−を・・・
うーむ。

自分としては、できる限り解析結果を再利用したいと。
その意味では木はいいんだよね。

んで、情報学科になって出てきた授業「アルゴリズムとデータ構造」の第2回(速すぎ(^^;))でスタックについてやったんだけど、そこで、「スタックの例として数式の解析を挙げます」とか。
んで、「スタックを使うと逆ポーランド記法の計算ができます。中置記法であるポーランド記法からもスタックを使えば逆ポーランド記法にできます。」
をを!これでいいじゃん。
スタックを使うのでちょっとメモリ管理が面倒だけど、階層になる気よりは1次元的なスタックのほうがラク!

次の手順で行う。
1−字句解析、数値、演算子、カッコ、変数の分離。
2−一応マイナスが単項演算子か2項演算子かチェック、アルファベット文字列が変数か関数かチェック。
3−逆ポーランド記法に並べ替え。
4−順にスタックを使って計算。


ちなみに、ここで変数の処理方法。
ハッシュとかを使ってもいいがとりあえず簡易化のために変数名の文字列の配列と、実際のデータの配列を作成し、単に線型探索(ってi=1から順に調べるだけだけど)で探すことに。
必要ならあとで高速化しておこう。

マイナスのチェックとしては次の方法で行う。
一番はじめに来ている、もしくは直前が左カッコなら単項演算子。
それ以外は2項演算子。
要は左側に演算対象があるかどうかなんだけどさ。

変数か関数かのチェックは右側にカッコがあるかどうか。
少なくとも配列に対応するつもりはないんで。
しかし考えてみるとCみたく配列と関数はカッコの種類変えたほうがコンパイラとしてはわかりやすいかもね。
しかもVBはCみたく関数定義とかもないからコンパイラがツラそう(^^;)

んで、逆ポーランド記法への変換方法。
まずは1の字句解析の結果を順にチェックしていく。
1−数値ならすぐに出力。
2−演算子はスタックにpushするが、スタックのtopの演算子の方が優先順位が低ければスタックのtopの演算子は出力。
3−左カッコはスタックにpush。
4−右カッコが来たら左カッコまでpopして出力。
授業ではここまでだったけど、単項演算子のマイナス、関数の処理のために左カッコよりもうひとつ先までpopしてチェックすることにした。
関数はともかく単項演算子のマイナスぐらいチェックしたほうがいいと思うぞ。

んで、逆ポーランド記法での実際の計算方法。
数値ならスタックにpush。
単項演算子、もしくは関数ならスタックからpopしたデータに対して計算し、結果を再びpush。
2項演算子なら2回popし、(2度目のpop)(演算子)(1度目のpop)の計算をし、結果をpush。
文法が合ってれば最後はスタックに1つしかデータがないはずなのでそれが答え。

ただ、メンドウなことに数式での変数代入機能をつけてしまったので、2項演算子でありながら実際の計算をするまで(関数、もしくは演算子の計算対象になるまで)は変数を数値化できなくなってしまった・・・

んで、ためしに作成。
ん?
なんか変だぞ?
3-5+4ならば当然2になるはず。なぜか-6。
逆ポーランド記法になってる状態を見ると、3 5 4 + -になってた。
3 5 - 4 +でなければいけないのに・・・
授業のノートを見る・・・やはり間違ってない。
んで、調べていくと・・・逆ポーランド記法にする時の演算子の処理で
「スタックのtopの演算子の方が優先順位が低ければ
ってのがあったけど、
「スタックのtopの演算子の方が優先順位が同じもしくは低ければ
にしないと上手く行かないじゃん。
授業中にちゃんと言ってくれ。

ムダにキャッシュ機能をつけてみる。
要は同じ数式が入力されたら以前の解析結果(計算結果ではない)を用いるというもの。
具体的には逆ポーランド記法への変換が終わったところで保存される。
こうすれば変数を変えるだけの場合(今回はモジュールではなくクラスで作ったが、変数はメソッドでも"a=2"みたいな数式でも変更化)高速化がみこめるかと。
数式が複雑なほど有効。

んで、適当に考えた次の数式をx=-10〜10で0.005刻み、つまり4000回計算してグラフを書いてみた。
y=(10+x)^(1/3)-exp(sin((x+5)/5))+sin(cos(x)+x)*x
キャッシュ有り−4.95s
キャッシュ無し−9.73s

ちなみに、VBのコードとして直接上の数式を入力すると
IDE環境下−0.22s
コンパイル後−0.17s
うーむ、やはりほとんどが数式の処理・・・計算そのものはほとんどない。
1回の計算なら1msとかって事になるけど、グラフみたいに何百何千と計算するには厳しい。

んで、今まで変数、演算子、関数を文字列で処理してたので高速化するためにID化をする。
ただ、これをやると拡張がしづらいんだよねぇ。
少なくとも自分で使う分にはいいんだけど、他の人が使うんなら・・・ということで。
一応拡張しやすさを少し残しつつ演算子、関数をまずID化。

問題は変数だが、結局「配列の要素番号」そのものをID化することにした。
もしセットされていない変数が出てきたら「その場で作成してID出力」(^^;)

んで、改良後。
キャッシュ有り−3.35s
キャッシュ無し−8.79s
まぁ少しマシになったかな。

ポインタが使えないんでメモリ操作がかなり頻繁にあることもあってこの速度かと。
まぁしょうがないか。
文字列をコピーしまくりだから、文字列データ抜き用の構造体も作ってみようかな。
あと、スタック操作で毎回Redimしてるけど考えたら最初から最大数取っとけば早かったな(^^;)
yaccとかlex使うとラクなのかなぁと思いつつも、来年にはコンパイラ作成の実習があるんでマジメにやっておかなくては。

00/10/05 アップグレード編
そろそろHDDが残りわずかになってきた。
教習所も終わったことだし、あと数日冬学期まで時間があるので秋葉原に。

半年ほど前には\13000で15GBのHDDを買ったけど、今では同じ値段で20GB〜25GBは買える。
んで、歩いてみた。

データ貯蔵用なので速度は遅くてもいいから要領の大きいものを。
んで、sofmapで45GBのHDD、\16000ナリ。

んで、もうひとつ欲しかったのがCPUorグラフィックボード。
CPUはK/6-2 400MHz(350からクロックアップ)だったんでマザーボード上限の550MHzにしたかったのね。
ただ・・・sofmap等メジャーどころを見るとPentV、Celeronばかり。
AMDでもAthlonとかDuron。
Duron・・・600MHzも\8000か・・・
T-ZONEではK/6-2 550MHzリテールを見かけたけど。。。\9999。
高い。

グラフィックボードにしようかなぁ・・・
うーむ、VRAM32MBでも\8000〜\40000まで色々あるなぁ。
GeForce256とGeForce2のどっちがいいのかよくわからんし、VooDooとかRageとかわけわからん。
さしあたって\10000以下のものでさしあたって買ってもいいけど・・・

んで、本通りよりちょっと外れたところを探してみる。
K/6-2 500MHz発見。
\8000・・・ちょい高い。
以前は\7000程度だったはずなのに。
まぁ見かけないからしょうがない。購入。


んで家に買える。
まずCPUから。
CPUの交換はやったこと無いけど・・・
ファンはなんか押したら動くとこがあったからいじってたら取れた。
んで、CPUが見える。
おお、確かにおんなじだ。
んで、500MHz装着。
IBMのページにあったようにFSBは100だから倍率を上限の5.5にする(をい)
起動!

ん?
BIOSでみると500MHzなんだけど。
しかも・・・起動と同時にエラー連発(T-T)
Safe Modeすら役に立たない。

ファンが弱いか?とか思いつつさしあたって450MHzで起動。
問題無し。
んで、先ほどのIBMの資料を見ると・・・

一応スイッチの倍率部分でx5.5に出来るものの、下にあった実際のCPU設定のサンプルではなぜか500MHzまでしか書いていない。
っていうか、マザーボード自体に5.0までしか書いてないしぃ(T-T)
ダ、だまされた・・・(そうか?)
さしあたって550MHz買わなくて良かった。
ただ、これなら450MHzで十分だったなぁ(をい)
実際ベンチマークにかけたところ、400MHzから500MHzになっただけあって整数演算、浮動小数点演算共に約1.25倍。
まぁ全く持ってそのとおりであるべきなんだろうけどちょっと悔しい。
まぁ、今まで20FPSでやってたゲームが30FPSになったからいいや。

んで、こんどはHDD。
今まではIDE-Primaryに6GB(boot、OS用)と15GB(7.5GB*2、データ用)があり、SeconderyにCDドライブがあったのね。
ただ、Seconderyの方のIDEバス(だっけ?)に2つ付けられる様になってなかったのでさしあたってCDを外して起動。
んで、まず15GBHDDにあるデータを45GBHDDに移さないと。

たらたらとFDISKとフォーマット。
45GBとか言いながらなぜか42500MBぐらいなんだけど・・・
パーティション区切ってあるからまず7.5GB分移動。

・・・・?
終了まで273分?
実際30分たっても1・2割しか進まなかった。
ためしに300MBぐらい動かすと・・・22分。
んん?
6GBHDDと15GBHDDでは300MBも1分かからなかったぞ?
ためしにHDBENCHにかけてみる。(注:HDDに無かったから電源切ってHDDとってCDつけて起動して解凍して電源切ってCDとってHDDつけて起動)
んん?HDBENCHは問題無し。むしろ45GBHDDの方が早い・・・当然だが。

んで、思いついたのはコピー元、コピー先ともにIDEの同じ方にしてみれば?ということ。
電源切ってごちゃごちゃいじって再起動。
7.5GB分移動・・・をを、8分ってでてる。まぁこんなもんだろうな。

・・・。
なぜか4分ぐらいして目盛が最後まで行ったのに終わらない。
っていうか目盛が元に戻って710000分とか出てきたんだけど(^^;)
しばらく待ったら突然「残り1分」になったけど。
結局8分ぐらいかかったかな。

もう7.5GBも移す。
こっちも一度1100000分とかになった・・・なんなんだ一体。
signedとunsignedの関係か?
32bitならミリ秒単位で時間を計測してたらそんなもんんかもしれないが。

んで、6GBから15GBにデータを移す。
もちろんIDE-Primary同士で。
さすがにファイルが多いからなあ・・・・
全部で36000ファイルほどあったが、うち7000がC:\Program Files\DevStudioで、5000がLaTeXだった。
コピーに1時間半かかった。TV見ながらやってたけど。

起動ディスクを作り、6GBHDDを外して15GBと45GBをIDE-Primaryにして起動。
ジャンパピンをごちゃごちゃいじってたら爪が痛い・・・
FDからFDISKして完了。

問題無し。
HDDも一気に増えたし。

うちのマザーボードではCPUは500MHzが限界なので、あと半年か1年ぐらい粘ったら買い替えかもね。
とはいえ、メモリは192MBあれば当分平気だし、HDDも60GBあればしばらく大丈夫だろうかと。
なんで、マザーボード、CPU、グラフィックボード(←これが一番緊急)だけ買いかえればさしあたって良い。
5万〜7万程度。アルバイト2・3ヶ月でどうにかなるかと。
まあCD-Rやスキャナ(←思いっきり持ち腐れになるって)も欲しいけどさ。

なお、K/6-2 350MHzはYahoo!オークションでさばきます。
6GBHDDは・・・同じかな。
次のHDDの費用に(をい)


一覧へ戻る