module JankenAsobi class Player def initialize @wincount = 0 end attr_reader :wincount, :name def count @wincount += 1 end end class Cp < Player def initialize @name = "Computer" super end def show_hand rand(3) end end class Man < Player def initialize print "あなたの名前を入力して下さい:" @name = gets.chomp @name = "名無し" if !@name or @name.empty? super end def show_hand begin print "#{@name}の手を入力して下さい(0:グー, 1:チョキ, 2:パー):" n = gets.chomp end until n == "0" or n == "1" or n == "2" n.to_i end end class Judge Hand = ["グー", "チョキ", "パー"] def initialize(p1, p2) @player1 = p1 @player2 = p2 puts "#{@player1.name} 対 #{@player2.name} :じゃんけん開始\n " end def game(n) puts "*** #{n}回戦 ***" hand1 = @player1.show_hand hand2 = @player2.show_hand judgement(hand1, hand2) end def judgement(h1, h2) winner = @player1 print "#{Hand[h1]} 対 #{Hand[h2]}で " if h1 == h2 puts "引き分けです。" return elsif (h1 - h2) % 3 == 1 winner = @player2 end puts "#{winner.name}の勝ちです。" winner.count end private :judgement def winner p1 = @player1.wincount p2 = @player2.wincount finalwinner = @player1 print "\n*** 最終結果 ***\n#{p1} 対 #{p2} で " if p1 == p2 puts "引き分けです。" return elsif p1 < p2 finalwinner = @player2 end puts "#{finalwinner.name}の勝ちです。" end end def self.play player1 = Cp.new player2 = Man.new judge = Judge.new(player1, player2) 5.times {|i| judge.game(i + 1)} judge.winner end end JankenAsobi.play
じゃんけんゲームで C# のオブジェクト指向プログラミングのお勉強
以前は OOP のお勉強に本のじゃんけんゲームのコードを移植してやっていたのですが、自分で作ってみることにしました。昨日一日で C# のお勉強をした(参照)ので、その試しです。あとで同じのを Ruby で書いてみるつもりです(追記:書きました)。
C# のコードです。えらく長くなってしまいました。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace JankenAsobi { class Program { static void Main(string[] args) { Cp player1 = new Cp(); Man player2 = new Man(); Judge judge = new Judge(player1, player2); for (int i = 1; i <= 5; i++) { judge.Game(i); } judge.Winner(); Console.Read(); } } abstract class Player { public int wincount; public String name; public String Name { get { return name; } } public Player() { this.wincount = 0; } public abstract int ShowHand(); public void Count() { wincount++; } public int WinCount { get { return wincount; } } } class Cp : Player { public Cp() { this.name = "Computer"; } public override int ShowHand() { Random rnd = new Random(); return rnd.Next(0, 3); } } class Man : Player { public Man() { Console.Write("あなたの名前を入力して下さい:"); this.name = Console.ReadLine(); if (name == "") { name = "名無し"; } } public override int ShowHand() { int n; do { Console.Write(this.name + "の手を入力して下さい(0:グー, 1:チョキ, 2:パー):"); try { n = int.Parse(Console.ReadLine()); } catch { Console.WriteLine("数字を入れて下さい"); n = -1; } } while (n != 0 && n != 1 && n != 2); return n; } } class Judge { public Player player1; public Player player2; public Judge(Player p1, Player p2) { this.player1 = p1; this.player2 = p2; Console.WriteLine(player1.Name + " 対 " + player2.Name + " :じゃんけん開始\n"); } public int hand1, hand2; public void Game(int n) { Console.WriteLine("*** {0}回戦 ***", n); hand1 = player1.ShowHand(); hand2 = player2.ShowHand(); Judgement(hand1, hand2); } private void Judgement(int h1, int h2) { Player winner = player1; Console.Write(Hand(h1) + " 対 " + Hand(h2) + "で "); if (h1 == h2) { Console.WriteLine("引き分けです。"); return; } else if ((3 + h1 - h2) % 3 == 1) { winner = player2; } Console.WriteLine(winner.Name + "の勝ちです。"); winner.Count(); } private string Hand(int h) { string[] hs = {"グー", "チョキ", "パー" }; return hs[h]; } public void Winner() { int p1, p2; p1 = player1.WinCount; p2 = player2.WinCount; Player finalwinner = player1; Console.Write("\n*** 最終結果 ***\n{0} 対 {1} で ", p1, p2); if (p1 == p2) { Console.WriteLine("引き分けです。"); return; } else if (p1 < p2) { finalwinner = player2; } Console.WriteLine(finalwinner.Name + "の勝ちです。"); } } }
- Program
- Main(): 最初に呼ばれるメソッドです。
- Player: 抽象クラスです。
- ShowHand(): 抽象メソッドです。手を見せます。
- Count(): 勝ったときに呼ばれて、勝数を 1増やします。
- Name: プロパティです。プレーヤーの名前を返します。
- WinCount: プロパティです。プレーヤーの勝った回数を返します。
- Cp: Playerクラスを継承します。対戦相手のコンピュータ側の処理をします。
- Man: Playerクラスを継承します。人間側の処理をします。
- Judge: 審判のクラスです。ゲームを進行させていきます。
- Game(int n): 第 n 回戦を進行させるメソッドです。
- Judgement(): private メソッドです。第 n 回戦の勝ち負けの処理をします。
- Winner(): すべての勝負が終わったあとの処理(最終勝者の処理など)をします。
一応 exe ファイルを添付しておくので、Windows を使っておられる方はダウンロードしてダブルクリックで遊べます(しょうもないものですけれど)。ただ、出所不明のアプリということで、いっぱい警告が出ると思いますが(笑)。
JankenAsobi.exe
GTK+ で落書き 6(Ruby)
Gem 'oekaki' で落書きです。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
スターを描いてみました。
require 'oekaki' Oekaki.app do draw do color(0, 0, 0) rectangle(true, 0, 0, 300, 300) color(0xdc00, 0xdc00, 0xdc00) #gainsboro arc(true, 0, 0, 300, 300, 0, 64 * 360) color(0x6b00, 0x8e00, 0x2300) #olivedrab star(true, 150, 150, 150, 0) end end
メソッド Tool#star(fill, x1, y1, x2, y2, color = nil) を新たに書きました。fill は ture で塗りつぶし、false で輪郭のみ描画します。(x1, y1) はスターの中心、(x2, y2) は(とがった)頂点のひとつの座標です。
こんなのも。
require 'oekaki' Oekaki.app width: 400, height: 400 do draw do color(0, 0, 0) rectangle(true, 0, 0, 400, 400) color(0, 65535, 0) po = Vector[0, 50] θ = PI / 15 a = Matrix[[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] for y in 0..3 for x in 0..3 x1 = x * 100 + 50 y1 = y * 100 + 50 star(false, x1, y1, x1 + po[0], y1 - po[1]) po = a * po end end end end
Gem の内部で require 'matrix', include Math をしているので、それらのクラスとモジュールがそのまま使えます。
Gem 'oekaki' に加えたコードはだいたいこんな感じです。
require 'matrix' include Math module Oekaki class Tool def star(fill, x1, y1, x2, y2, color = nil) set_color(color) Star.new(fill, x1, y1, x2, y2, @color).draw end end class Star < Tool def initialize(fill, x1, y1, x2, y2, color) @fill = fill @o = []; @a = []; @b = [] @o[0], @o[1] = x1, y1 @a[0] = Vector[x2 - x1, y1 - y2] θ = PI / 5 rt1 = Matrix[[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] rt2 = rt1 * rt1 1.upto(4) {|i| @a[i] = rt2 * @a[i - 1]} t = cos(2 * θ) / cos(θ) @b[0] = rt1 * @a[0] * t 1.upto(4) {|i| @b[i] = rt2 * @b[i - 1]} super() @color = color end def draw_triangle(n) ar = [@a[n].to_w(@o), @b[n].to_w(@o), @b[(n - 1) % 5].to_w(@o)] polygon(@fill, ar) end private :draw_triangle def draw if @fill 5.times {|i| draw_triangle(i)} ar = [] 5.times {|i| ar << @b[i].to_w(@o)} polygon(@fill, ar) else ar = [] 5.times {|i| ar << @a[i].to_w(@o); ar << @b[i].to_w(@o)} polygon(@fill, ar) end end end end class Vector def to_w(o) v = self [o[0] + v[0], o[1] - v[1]] end end
スターのそれぞれの頂点は、頂点ベクトルに回転行列(変数 rt2)を順に掛けて求めています。Ruby では標準添付ライブラリの 'matrix' で行列やベクトルの計算が簡単に行なえます。
一番苦心したのがじつは色の取り扱いです。Tool#star メソッドの中で Starクラスのインスタンスを新たに作っているので、Tool#color を Tool#star に対して使うとき、Starクラスは Toolクラスを継承しているものの、Toolクラスのインスタンス変数の内容が Star.new で上書きされてしまいます。なので、少しトリッキーなことをしています。
「Gem 'oekaki'」タグを作りました。
ファイルが画像でなければ削除する(Ruby)
削除すれば true、しなければ false を返します。なお、判断は拡張子で行っているわけではありません。
irb(main):007:0> require './delete_non_img' => true irb(main):008:0> Utils.delete_non_img("img.png") #削除しない => false irb(main):009:0> Utils.delete_non_img("text") #削除する => true
delete_non_img.rb
require 'fileutils' require 'utils' module Utils def delete_non_img(fname) if Utils.imgexist?(fname) false else FileUtils.rm(fname) true end end module_function :delete_non_img end
モジュール 'utils' は自家製です(参照)。
GTK+ でプログレスバーを簡単に使う(Ruby)
長い処理だと、プログレスバーがあると便利ですよね。GTK+ で簡単に表示できるライブラリを作ってみました。
使い方はこんな感じです。
Utils.progress_bar do |bar| for i in 0..20 bar.fraction = i / 20.0 bar.text = "#{i * 5}%" sleep(0.5) end end
Utils.progress_bar
のブロック内に処理を書きます。ブロック変数 bar のインスタンス・メソッド Gtk::ProgressBar#fraction=
あるいは Gtk::ProgressBar#set_fraction()
がプログレスバーの値を決めるもので、0.0〜1.0 の値を代入して下さい。ウィンドウは自動で消えないので、ウィンドウに付いている消去のマークをクリックして処理が完了します。なお、ウィンドウを強制消去してしまった場合はユーザーの処理も中断してしまうので、注意して下さい。
なお、Gtk::PorgressBar
のインスタンス・メソッドはすべて使うことができます(text=
なども)。
Ruby コード。
require 'gtk2' module Utils def progress_bar w = Gtk::Window.new w.signal_connect("destroy") {Gtk.main_quit} w.set_size_request(300, 50) w.border_width = 10 w.title = "Progress" bar = Gtk::ProgressBar.new w.add(bar) Thread.new(bar) do |bar| yield(bar) end w.show_all Gtk.main end module_function :progress_bar end
なお、これは 野良 Gem 'Utils' - Camera Obscura に入れてあります。
GTK+ で落書き 5(Ruby)
Gem 'oekaki' で落書きです。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
キャンバス空間と色空間をリニアに連続的変化させて、ドットを描いています。
Ruby コード。
require 'oekaki' require 'matrix' L = 400 C_MAX = 65536 C_STEP = 1000 class Field def initialize @x = rand(L); @y = rand(L) n = Vector[rand, rand] @n = [n[0] * 2 / n.norm, n[1] * 2 / n.norm] end attr_reader :x, :y def next @x, @y = @x + @n[0], @y + @n[1] @n[0] = -@n[0] if @x < 2 or @x >= L - 2 @n[1] = -@n[1] if @y < 2 or @y >= L - 2 end end class Color def initialize init end attr_reader :x, :y, :z def init @x = rand(C_MAX); @y = rand(C_MAX); @z = rand(C_MAX) cn = Vector[rand, rand, rand] @cn = [cn[0] / cn.norm, cn[1] / cn.norm, cn[2]/ cn.norm] end def next @x, @y, @z = @x + @cn[0] * C_STEP, @y + @cn[1] * C_STEP, @z + @cn[2] * C_STEP @cn[0] = -@cn[0] if @x < C_STEP or @x >= C_MAX - C_STEP @cn[1] = -@cn[1] if @y < C_STEP or @y >= C_MAX - C_STEP @cn[2] = -@cn[2] if @z < C_STEP or @z >= C_MAX - C_STEP init if rand < 0.001 end end pos = Field.new col = Color.new Oekaki.app width: L, height: L do draw do color(0, 0, 0) rectangle(true, 0, 0, L, L) end timer(20) do color(col.x.to_i, col.y.to_i, col.z.to_i) arc(true, pos.x.to_i + 2, pos.y.to_i + 2, 4, 4, 0, 64 * 360) pos.next col.next true end end
だいぶ DRY原則に反したコードですが、お許しを。
ディレクトリのバックアップ-削除あり(Ruby)
Ruby でバックアップ・プログラムを書きました。バックアップ先のフォルダがバックアップ元のフォルダの中身と再帰的に同じ内容になるようにします。なので、バックアップ先にあってバックアップ元の中身にないファイルやフォルダは(バックアップ先で)削除されるので注意して下さい。「削除がされない」バージョンは下のリンク先にあります。
再帰的なファイルのバックアップ(Ruby) - Camera Obscura
なお、機械的なコピーではありません。変更があった部分のみ処理します。基本的に Linux 向けで、Windows ではファイルの日付の考え方が Linux とはちがうので、期待した動作をしません。
使い方はこんな感じです。backup('バックアップ元', 'バックアップ先')
backup('/home/***/Documents', '/media/***/MyHDD/Documents')
backup.rb
require 'fileutils' def backup(snd, rsv) #String{snd, rsv} -> String{r_fn[], s_fn[], s, r}, Time{mts, mtr} Dir.chdir(rsv); r_fn = Dir.glob("*") Dir.chdir(snd); s_fn = Dir.glob("*") r_fn.each do |fname| unless s_fn.include?(fname) FileUtils.rm_r(File.join(rsv, fname)) puts "delete: #{fname}" end end s_fn.each do |fname| s = File.join(snd, fname) r = File.join(rsv, fname) mts = File.stat(s).mtime mtr = nil mtr = File.stat(r).mtime if r_fn.include?(fname) if FileTest.directory?(s) if r_fn.include?(fname) backup(s, r) if mts > mtr and FileTest.directory?(r) else puts "folder copy: #{fname}" FileUtils.cp_r(s, r) end elsif !r_fn.include?(fname) or mts > mtr puts "file copy: #{fname}" FileUtils.cp(s, r) end end end