#!/usr/koeki/bin/ruby # -*- coding: utf-8 -*- require "curses" # 万有引力定数 G = 6.67430×10^-11 m^3/kg/s^2 # 地球の質量 ME = 5.9724×10^24 kg # 地球の半径 RE = 6.3781x10^6 m $G = 6.67430e-11 $ME = 5.9724e24 $RE = 6.3781e6 # 端末上の座標に変換するための x, y 方向のスケール $xscale = 10.0 $yscale = 5.0 # 初期値 # ($x0, $y0):ロケットの発射位置(単位 m) # ($u0, $v0):ロケットの発射速度(単位 m/s) # $dt:時間の刻み(単位 s) $x0 = $RE + 10 $y0 = 0 $u0 = 6000 $v0 = 6000 $dt = 60 # 原点から (x, y) までの距離 def r(x, y) return Math.sqrt(x ** 2 + y ** 2) end # 運動方程式の右辺1 def f(x, y) return - $G * $ME * x / r(x, y) ** 3 end # 運動方程式の右辺2 def g(x, y) return - $G * $ME * y / r(x, y) ** 3 end # ku, kv, kx, ky の計算 def calc_k(u, v, x, y, du, dv, dx, dy) return f(x + dx, y + dy) * $dt, g(x + dx, y + dy) * $dt, (u + du) * $dt, (v + dv) * $dt end # ルンゲ=クッタ法で $dt 後の (x,y) と (u,v) を求める def next_step(u, v, x, y) ku1, kv1, kx1, ky1 = calc_k(u, v, x, y, 0, 0, 0, 0) ku2, kv2, kx2, ky2 = calc_k(u, v, x, y, ku1 * 0.5, kv1 * 0.5, kx1 * 0.5, ky1 * 0.5) ku3, kv3, kx3, ky3 = calc_k(u, v, x, y, ku2 * 0.5, kv2 * 0.5, kx2 * 0.5, ky2 * 0.5) ku4, kv4, kx4, ky4 = calc_k(u, v, x, y, ku3, kv3, kx3, ky3) return u + (ku1 + 2 * ku2 + 2 * ku3 + ku4) / 6.0, v + (kv1 + 2 * kv2 + 2 * kv3 + kv4) / 6.0, x + (kx1 + 2 * kx2 + 2 * kx3 + kx4) / 6.0, y + (ky1 + 2 * ky2 + 2 * ky3 + ky4) / 6.0 end # 地球を描く def draw_earth Curses.attrset(Curses.color_pair(2)) 0.upto(Curses.cols - 1) do |cpx| 0.upto(Curses.lines - 1) do |cpy| if (r((cpx - $cox) / $xscale.to_f, (cpy - $coy) / $yscale.to_f) < 1) Curses.setpos(cpy, cpx) Curses.addch(' ') end end end Curses.attrset(Curses.color_pair(1)) end # (x, y) から端末上の座標へ変換して文字 ch を出力 def puts_char(x, y, ch) cpx = $cox + x / $RE * $xscale cpy = $coy - y / $RE * $yscale if (cpx >= 0 && cpx <= Curses.cols - 1) && (cpy >= 0 && cpy <= Curses.lines - 1) Curses.setpos(cpy, cpx) Curses.addch(ch) end end # Cursesの初期化 Curses.init_screen begin # 色の設定 Curses.start_color Curses.init_pair(1, Curses::COLOR_WHITE, Curses::COLOR_BLACK) Curses.init_pair(2, Curses::COLOR_BLACK, Curses::COLOR_BLUE) Curses.init_pair(3, Curses::COLOR_BLACK, Curses::COLOR_RED) # 端末の中央の座標 $cox = ((Curses.cols - 1) * 0.5).round $coy = ((Curses.lines - 1) * 0.5).round # カーソルを非表示 Curses.curs_set(0) # キー入力で待つ時間(ミリ秒) Curses.stdscr.timeout = 60 # 地球を出力 draw_earth Curses.refresh # 初期値を代入 x = $x0 y = $y0 u = $u0 v = $v0 i = 0 # 無限ループ while 1 # 時刻の出力 Curses.setpos(Curses.lines - 1, 0) Curses.addstr(sprintf("t=%d[s]\n", i * $dt)) # (x, y) に文字を出力 if (r(x, y) >= $RE) # ロケットを出力 puts_char(x, y, 'R') Curses.refresh else # 爆発を出力 Curses.attrset(Curses.color_pair(3)) puts_char(x, y, 'X') Curses.refresh break end # キー入力 if Curses.getch != nil break end # (x, y) にロケットの跡を出力 puts_char(x, y, '.') Curses.refresh # $dt 後の (x,y) を求める u, v, x, y = next_step(u, v, x, y) i += 1 end # キー入力があるまでずっと待つ Curses.stdscr.timeout = -1 Curses.getch ensure # Cursesの終了処理 Curses.close_screen end