Newer
Older
Ruby / last / j2102_last.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

#trrでなかなか成長しないのでいっそのこと、、っていうか。うーん?
#まあとにかく。自作ならやる気出るだろう。という、実に謎い思考回路。
#ところがどっこい、結局trrを参考にしているという矛盾。^^;むう。。


#このプログラムはタイピングです。
#自分にとって楽しく、または便利なタイピングが作りたいと思い、こうなりました。
#これを作ろうと思ったのは、私のような、タイピングを速くなりたいけど、何が課題かわからない人に
#役に立つものを作りたかったからです。
#きっかけは、自分がタイピングサイトを探していたときに、こんな機能があったらいいなと感じたことです。
#作ろうとした機能、考えた機能は、(つまり「あったらいいな感じた機能」)
#*自分のペースで練習ができる、ジャンル選択、回数選択
#*打つのにかかった時間、各キーでのミス回数、最高得点中何点で何%取れているのかの結果表示
#*CSVからランダムに出題、選択式のお題
#*楽しく打てる内容(早口言葉、メソッド名など)
#*アルファベットやローマ字をランダムに羅列して出題したり(予測したら負けモード)、
# ミスした文をやり直す(パーフェクト出すまで帰れませんモード)「正確さトレーニング」機能
#*とにかく文章を打ちまくる「素早さトレーニング」機能(ミスしても次に行く)
#*自分の打った文がお題になる「自習」機能
#です。100行までということで、かなり不完全で不自然な仕上がりですが、かろうじて
#どうしても作りたかった機能は実装できました。
#今回実現できたのは、
#*ジャンル選択、回数選択
#*楽しく打てる内容
#*ミス回数、どのキーでミスしたのかわかる
# 現状
#この時点では、点数も出ないし、結果が不十分な印象です。
#また、表示がずれたり、入力で想定外の値が入れられるとエラーや期待していない値が
#渡されたり、改善点が大量にあります。まだまだ完璧とは言えないです。
#今のアピールポイントは、csvを規則に従って書けば、問題がカスタマイズできることです。
#規則はソースコードとcsvを見れば割とすぐに理解できると思います。具体的には、
#まず、typing-texts.csvを開きます。
#アルファベット、記号でないものが含まれるジャンルの場合、ヘッダーに()をつけます。
#カッコの内容を変更した、アルファベット版も用意します。
#ヘッダーのカッコ以外の部分は元とおなじにしてください。内容ができたら、
#typing-texts-utu.csvにヘッダーの()部分を消したアルファベット版のデータを貼り付けてください。
#typing-texts.csvは画面に表示する、見かけ上打つ文字です。
#typing-texts-utu.csvは、実際にキーボードから打っている文字です。
#うまく書き込むと、新たなジャンルとして遊べるようになります。

# 他におもいついたアイデア
#一つは言葉の力で励ましたり勇気を出したりできるようなプログラムです。具体的には、
#診断形式で、今その人に響く言葉を導こうとするものです。
#他には、音で時間がわかる時計や、密集した字を行ごとに分けた表示で見やすくするなどです。





require 'curses'; include Curses
require 'csv'
sentence    = CSV.read("typing-texts.csv" , :headers => true)#タイピング本文
sentence_utu= CSV.read("typing-texts-utu.csv",:headers => true)
menu_sentakusi ={1=>"play",2=>"option",3=>"practice"}
play_sentakusi =sentence.headers
type_list =[] #打つ内容を呼び出すための文字列を格納する
kaisu = 0   # 問題数を指定
miss_now = nil      # 各文字でミスったか否か
miss_all = []      # 全部で何回、どの字でミスったか
class Window
  @@numbers = CSV.read("futaketa-alpha.csv",:headers => true)
def putSentakusi(nani,zip = @@numbers.headers)
  for na,s in nani.zip(zip)
    self.addstr(sprintf("%s => %s\n",s,na))
  end
  self.refresh
end
def getAns()
  self.addstr("入力:")
  self.refresh
  while true
    a = getch 
    if a == "q"
      return "q"
    elsif @@numbers[a]
      return @@numbers[a].join.to_i
   end
  end
end
def addstr_sousyoku(nani,str)
  nani.each{|n|self.attron(n)}
               self.addstr(str)
  nani.each{|n|self.attroff(n)}        
end
end
init_screen
noecho
cbreak
begin
  win = Window.new(32,60,10,10)
  win.addstr("タイピングです。\n番号を入れてください\n")
    win.addstr("ジャンルを決めてください(複数選択可能、qで入力終了\nもう一度選ぶと選択解除)\n")#refreshはquestionに
    cursor_position =[win.cury,win.curx]
    while true
      win.setpos(*cursor_position) ##* s e t "p  o" s *##setopsって書くとそのエラーすっごく見つけづらい
      win.putSentakusi(play_sentakusi)
      choice = win.getAns
      break if choice == "q"
      play_choice = play_sentakusi[choice-1]
      already_chosen = type_list.include?(play_choice)
      if play_choice
        type_list << play_choice #打つ内容に紐付いた文字を格納
        type_list.delete(play_choice)if already_chosen
      end
      win.addstr(sprintf("\n選択済み:%s\n",type_list.join(",")) ); win.refresh
    end
    odai_display_list = type_list.collect{|type|sentence[type]}.flatten.compact
    odai_utu_list = type_list.collect{|type|sentaku = type.sub(/\(.*?\)/,"").to_s;sentence_utu[sentaku]}.flatten.compact
    odai_length = odai_display_list.length  # 問題数を拾っている
    win.clear
    win.addstr(sprintf("何問やりますか(最大%d問)\n",odai_length))
    until kaisu != "q" && kaisu >= 1 && kaisu <= odai_length 
      kaisu = win.getAns
      win.setpos(1,0)
    end                  
    win.addstr("\nなにか打つと開始!!\n")
    win.getAns; refresh; win.setpos(0,0)
    number = 0  #お題の番号 #文字の様子 -> 入力前太字。入力後普通、ミス下線
    start_time = Time.now.to_i
    kaisu.times do
      number  = rand(0..(odai_length-1) )                   #この回のループで出すお題の番号
      odai = {display: odai_display_list[number],utu: odai_utu_list[number]}#この回のループで出すお題のハッシュ
      ch_count = 0 
      win.addstr_sousyoku([A_BOLD],odai[:display]+"\n")
      cursor_position =[win.cury,win.curx]
      win.addstr_sousyoku([A_DIM,A_BOLD],odai[:utu])
      while odai[:utu].length > ch_count
        win.setpos(cursor_position[0],(cursor_position[1])+ch_count);win.refresh
        input = getch 
        if input == odai[:utu][ch_count]
          win.addch(odai[:utu][ch_count])unless miss_now
          win.addstr_sousyoku([A_UNDERLINE],odai[:utu][ch_count])if miss_now
          ch_count += 1 ;miss_now = false ; win.refresh
        else
          miss_all << odai[:utu][ch_count] ; miss_now = true
        end
      end
      win.clear ; odai_length -= 1
      odai_display_list.delete_at(number)#2回出題を防ぐ
      odai_utu_list.delete_at(number)##for最終行##
    end
    keikajikan = (Time.now.to_i) - start_time
    miss = miss_all.tally
    win.addstr_sousyoku([A_BOLD],"結果\n")
    win.addstr(sprintf("かかった時間: %d秒(1問あたり: %d秒),ミス: %d回\nミスの詳細\n",keikajikan,keikajikan/kaisu,miss_all.length))
    win.putSentakusi(miss.values,miss.keys); refresh
ensure
  close_screen
end