echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【やってみた】1時間プログラミング(ソースコード)

昨日書いた、オセロのソースコードです。

※ワークシート側の処理

Option Explicit

'「ゲーム開始」ボタンクリック処理
Private Sub CommandButton1_Click()
    
    Call Init
    
End Sub

'盤上のマスをダブルクリックしたときの処理
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)

    'セルの行と列
    Dim row As Integer, col As Integer
    Dim resultValue As Boolean, resultCheckAll As Integer
    
    row = Target.row
    col = Target.Column
    
    '盤の外とか、すでに石が置いてあるマスには置けない。
    If ((8 < row) Or (8 < col)) Then
        Exit Sub
    ElseIf (CELLKIND.NOPEACE <> board(row, col)) Then
        MsgBox "すでに石がある場所には置けません。", vbOKOnly + vbExclamation, GAME_NAME
        Exit Sub
    End If

    '現在の手番のプレーヤーが、そこに石を置けるかのチェック
    resultValue = checkPutPeace(row, col, True)
    
    If resultValue Then
        '置けた場合、相手が石を置けるマスがあるかをチェック
        resultCheckAll = checkPutPeaceAllCell()
        
        If (1 = resultCheckAll) Then
        
            'マスがない場合、パス扱いにして、次の手番のプレーヤーが石を置ける益があるかチェックする。
            Dim strTeban As String
            
            If (CELLKIND.SENTE = turn) Then
                strTeban = "先手"
            ElseIf (CELLKIND.GOTE = turn) Then
                strTeban = "後手"
            End If
            
            MsgBox strTeban & "は相手の石を裏返せないので、パスします。", vbOKOnly + vbInformation, GAME_NAME
            
            turn = 3 - turn
            resultCheckAll = checkPutPeaceAllCell()
            
            If (1 = resultCheckAll) Then
                '2人共石を置けない場合は、試合終了。勝敗判定へ。
                If (CELLKIND.SENTE = turn) Then
                    strTeban = "先手"
                ElseIf (CELLKIND.GOTE = turn) Then
                    strTeban = "後手"
                End If
                
                MsgBox strTeban & "も相手の石を裏返せないので、ゲームを終了し、勝敗を判定します。", vbOKOnly + vbInformation, GAME_NAME
                Call judge
            End If
        ElseIf (-1 = resultCheckAll) Then
            'マスが全部埋まった場合、勝敗判定へ。
            Call judge
        End If
    Else
        'ダブルクリックした場所に置いても相手の石を裏返せない場合、そこには置けない。
        MsgBox "相手の石を裏返せないので、そこには置けません。", vbOKOnly + vbExclamation, GAME_NAME
    End If
    
End Sub

'石を置いた時、相手が石をおけるマスがあるかどうかをチェックする。
Private Function checkPutPeaceAllCell() As Integer

    Dim i As Integer, j As Integer
    Dim isPut As Boolean, isExistEmpty As Boolean
    
    isPut = False
    isExistEmpty = False
    
    For i = 1 To 8
        For j = 1 To 8
            
            If (board(i, j) = CELLKIND.NOPEACE) Then
                '石が置いてないマスがあったら、isExistEmptyフラグを立てる。
                '(全部のマスに石が置いてあるなら、相手の処理を行わずに勝敗判定を行えば良いから。)
                isExistEmpty = True
                isPut = checkPutPeace(i, j, False)
            End If
            
            '1つでも石を置けるマスがあるなら、そこで処理終了。(下も同じ)
            If (isPut) Then
                Exit For
            End If
        Next
        
        If (isPut) Then
            Exit For
        End If
    Next

    '戻り値の設定
    If (isPut) Then
        '石を置けるマスがある。
        checkPutPeaceAllCell = 0
    Else
       If (isExistEmpty) Then
           '相手が石を置けるマスはないが、石が置いていないマス自体はある。
           checkPutPeaceAllCell = 1
       Else
           '全部のマスが埋まっている。
           checkPutPeaceAllCell = -1
       End If
    End If
    
End Function



※標準モジュール側の処理

Option Explicit
Option Base 0

'「インデックスが有効範囲にありません」エラーのエラー番号
Const INDEX_OUT_OF_RANGE As Integer = 9
'メッセージボックスに表示するタイトル
Public Const GAME_NAME As String = "リバーシ"

'盤の状態を保持する2次元配列
Public board(1 To 8, 1 To 8) As CELLKIND
'手番の状態
Public turn As CELLKIND

'盤や手番の状態を定義してある列挙体
Public Enum CELLKIND
    NOPEACE = 0    '石がない
    SENTE = 1      '先手
    GOTE = 2       '後手
    GAME_END = -1  'ゲーム終了
End Enum

'checkReverse()で使用する、各方向のオフセット値を格納する配列
Private range_value(0 To 7)
'裏返すマス情報を保持するコレクション(裏返せるマス全て, 方向単位の裏返せるマス)
Private turnCellCollection As New Collection, turnCellCollection_tmp As New Collection

'初期化処理
Public Sub Init()
On Error Resume Next

Dim i As Integer, j As Integer

'各方向のオフセット値を設定
range_value(0) = Array(-1, -1)
range_value(1) = Array(-1, 0)
range_value(2) = Array(-1, 1)
range_value(3) = Array(0, -1)
range_value(4) = Array(0, 1)
range_value(5) = Array(1, -1)
range_value(6) = Array(1, 0)
range_value(7) = Array(1, 1)

For i = 1 To 8
    For j = 1 To 8
     
     '盤の情報を設定。
    If (((4 = i) And (4 = j)) Or ((5 = i) And (5 = j))) Then
        board(i, j) = CELLKIND.SENTE
    ElseIf (((4 = i) And (5 = j)) Or ((5 = i) And (4 = j))) Then
        board(i, j) = CELLKIND.GOTE
    Else
        board(i, j) = CELLKIND.NOPEACE
    End If
    
    Call draw(i, j)
    
    Next
Next

turn = CELLKIND.SENTE

Set turnCellCollection = New Collection
Call writeText

End Sub

'そのマスに手番の石が置けるかどうか。
'(isPutActuallyはTrueなら実際に裏返す。falseなら裏返せるかチェックするだけで、実際には裏返さない)
Public Function checkPutPeace(ByVal row As Integer, ByVal col As Integer, ByVal isPutActually As Boolean)
   
   Dim resultValue As Boolean, isExistTurn As Boolean
   Dim i As Integer, j As Integer
   
   isExistTurn = False
   
   'そもそも石があるなら、絶対に置けない。
   If (CELLKIND.NOPEACE <> board(row, col)) Then
       checkPutPeace = isExistTurn
       Exit Function
   End If
   
   For i = LBound(range_value) To UBound(range_value)
       resultValue = checkReverse(row + range_value(i)(0), col + range_value(i)(1), range_value(i)(0), range_value(i)(1), True)
       
       '石が置ける、かつ実際に裏返すなら、「裏返すマス」に「その方向の裏返せるマス」を追加。
       If resultValue Then
           If (isPutActually) Then
               For j = 1 To turnCellCollection_tmp.count
                   turnCellCollection.Add turnCellCollection_tmp(j)
               Next
           End If
           
           isExistTurn = True
       End If
       
       '「その方向の裏返せるマス」を初期化。
       Set turnCellCollection_tmp = New Collection
       
       '実際には裏返さない場合、1つでも石を置けるマスがあった時点で処理終了。(全チェックする必要はない)
       If ((Not isPutActually) And isExistTurn) Then
           Exit For
       End If
   Next
   
   If (isExistTurn And isPutActually) Then

       '実際に裏返す場合、盤の情報を更新して、手番を交代する。
       If (0 < turnCellCollection.count) Then
           
           For i = 1 To turnCellCollection.count
           
               Dim collection_val As Variant, collection_row As Integer, collection_col As Integer
               
               collection_val = turnCellCollection(i)
               collection_row = collection_val(0)
               collection_col = collection_val(1)
               
               board(collection_row, collection_col) = turn
               Call draw(collection_row, collection_col)
           Next
       End If
       
       board(row, col) = turn
       Call draw(row, col)
       
       turn = 3 - turn
       Call writeText
       
   End If
   
   '「すべての裏返せるマス」を初期化。
   Set turnCellCollection = New Collection
   checkPutPeace = isExistTurn
   
End Function

'「そのマスの石が裏返せるか」をチェックする。
'range_value_row,range_value_colはそれぞれ基準のマスから見たチェックするマスのオフセット、isFirstChackはそのマスが基準となるマスの隣かどうか。
Function checkReverse(ByVal row As Integer, ByVal col As Integer, ByVal range_value_row As Integer, ByVal range_value_col As Integer, ByVal isFirstCheck As Boolean) As Boolean
On Error GoTo checkReverseException

    Dim targetCellValue As CELLKIND
    Dim resultValue As Boolean
    
    'チェックするマスの情報を格納
    resultValue = False
    targetCellValue = board(row, col)
    
    If (isFirstCheck) Then
        '基準のマスの隣のマスが相手の石でなければ裏返せない。相手の石ならさらに次のマスを再帰処理でチェック。
        If ((turn <> targetCellValue) And (CELLKIND.NOPEACE <> targetCellValue)) Then
            turnCellCollection_tmp.Add Array(row, col)
            resultValue = checkReverse(row + range_value_row, col + range_value_col, range_value_row, range_value_col, False)
        End If
    Else
        '隣のマスではない場合
        If (turn = targetCellValue) Then
            '自分の石があったら、そこまでの石は裏返せる。(=挟んでいる状態)
            resultValue = True
        ElseIf (CELLKIND.NOPEACE = targetCellValue) Then
            '自分の石の前に何もないマスがあったら、裏返せない。
            resultValue = False
        Else
            '相手の石の場合は、さらに次のマスをチェック。
            turnCellCollection_tmp.Add Array(row, col)
            resultValue = checkReverse(row + range_value_row, col + range_value_col, range_value_row, range_value_col, False)
        End If
    End If
        
    checkReverse = resultValue
    Exit Function
    
checkReverseException:
    '「(board(row, col)の)インデックスが有効範囲にない」エラー。(一番端まで相手の石しか無い場合)。
    'その場合、もちろん裏返せない。
    If (INDEX_OUT_OF_RANGE = Err.Number) Then
        Debug.Print "abc"
        Err.Clear
        resultValue = False
    Else
        MsgBox Err.Number & " " & Err.Description
        resultValue = False
    End If
End Function

'勝敗判定
Public Sub judge()

    Dim count_sente As Integer, count_gote As Integer
    Dim strMessage As String
    
    turn = CELLKIND.GAME_END
    count_sente = countPeace(CELLKIND.SENTE)
    count_gote = countPeace(CELLKIND.GOTE)
    
    strMessage = "先手:" & count_sente & " 対  後手:" & count_gote & vbCrLf
    If (count_sente > count_gote) Then
        strMessage = strMessage & "先手の勝ち!"
    ElseIf (count_sente < count_gote) Then
        strMessage = strMessage & "後手の勝ち!"
    Else
        strMessage = strMessage & "引けわけです。"
    End If
    
    MsgBox strMessage, vbOKOnly + vbInformation, GAME_NAME
    Call writeText
    
End Sub

'tebanで指定された手番の石の数を調べる。
Private Function countPeace(ByVal teban As CELLKIND) As Integer

    Dim count As Integer, i As Integer, j As Integer
    count = 0
    
    For i = 1 To 8
        For j = 1 To 8
            If teban = board(i, j) Then
                count = count + 1
            End If
        Next
    Next
    
    countPeace = count

End Function

'盤を描画する。
Private Sub draw(ByVal row As Integer, col As Integer)
    
    If (CELLKIND.SENTE = board(row, col)) Then
        Sheets("Sheet1").Cells(row, col).Value = "○"
    ElseIf (CELLKIND.GOTE = board(row, col)) Then
        Sheets("Sheet1").Cells(row, col).Value = "●"
    Else
        Sheets("Sheet1").Cells(row, col).Value = ""
    End If

End Sub

'右フレームのテキストを描画する。
Public Sub writeText()

    If (CELLKIND.SENTE = turn) Then
        Range("J7").Value = "先手(白) の番です。"
    ElseIf (CELLKIND.GOTE = turn) Then
        Range("J7").Value = "後手(黒) の番です。"
    ElseIf (CELLKIND.GAME_END = turn) Then
        Range("J7").Value = "ゲーム終了!"
    End If
    
    Range("J3").Value = "先手(白): " & countPeace(CELLKIND.SENTE)
    Range("J4").Value = "後手(黒): " & countPeace(CELLKIND.GOTE)
    
End Sub

詰めが甘い部分もあるとは思いますが(特にcheckPutPeace()の一連の処理など)、ひとまずはこんな感じでしょうか。

http://ecx.images-amazon.com/images/I/51P83V533KL._SL75_.jpg
本格的に作るなら、こういう本で思考アルゴリズムとか勉強したら、もっと面白くなるんでしょうね。
(※上記のソースコードに、対CPU戦の機能はありません。)

【やってみた】1時間プログラミング

皆さんは、この動画を知っているでしょうか?

www.nicovideo.jp

IT系の書籍でもおなじみ、紀平拓男さんの動画で、タイトル通り「1時間以内にオセロをプログラミングしちゃおう」というものです。
一時期、IT系のニュースサイトなどでも話題になりました。

近頃は日経ソフトウェアの記事を読むたびに「ちょっとExcel VBAがおざなりになってるなあ…」と感じていたこともあったので、(無謀にも)リハビリがてら、これに挑戦してみました。

結果は…


テラ無理ゲー


確かに「石橋を叩きまくる」性格からか、決してプログラミングは早い方ではないとは自覚していましたが、流石に一時間は凄すぎます。
僕はというと、2時間でやっとそれっぽい動きをさせるのがやっとでした。(もちろんバグもある状態で)
しかも紀平さんは、環境設定(ブラウザインストールなど)からやってるし、HTML+JavaScriptで作成してたので、描画処理とかもやってるわけですから(僕はExcel VBAなので、オセロ盤の処理がJavaScriptより多少簡単に処理できる。)

いやホント、すごい人はいるもんだなあ、と実感しました。

でも、こういうすごい人がいるからこそ、刺激にもなるし勉強にもなるし、あと「○○では絶対に勝てないから、別で勝てるような事を身に付けなくちゃ」って思えるんですけどね。
それにこの業界は、面白い技術が次から次に生まれますし。

ホント、この業界を選んで良かった、って思います。
まあ「理系の大学に行って、本格的にITとか技術系の勉強をしとけばよかった」ってのは、今でも思いますけどね。

以上、本格的にITに触れたのは社会人になってからの、(遊べない文学部出身の)文系エンジニアの願望でした。


※おまけ
f:id:Makky12:20161011212933p:plain
結局3時間以上かかりましたが、ちゃんと最後まで完成させました。
ソースコードは後日アップロードしようと思います。)

フリーになる時に必要な手続き その2

というわけで、前回の続きです。

 

4.専用の口座&クレジットカード

これは「必須」ってわけではないですが、あったほうがいいと思います。帳簿をつける以上、仕事に関する事のお金の出入りはしっかり管理しないといけないですし、その際にプライベートの口座と一緒だと、ごっちゃになって管理がしにくくなりますし。

また万が一、確定申告で通帳しか証拠がない…なんて時に、プライベートの口座と一緒だと、あんな事やこんな事での利用歴を見せるハメに…なんてことも、無いとは言い切れない(?)ですし。

 

あと、そういう意味でも事業専用のクレジットカードも作っておいたほうが良いと思います。それに経費って結構出費が大きいので、どうせならこの機会にポイントとかマイレージとかのサービスが充実したものに加入するのが良いかも、って思ってます。

そうそう、フリーランス系のサイトでもよく書かれていますが「サラリーマンより審査が厳しい」みたいな話もよく聞きますので、できれば退職前に申し込んでおくのをオススメします。

 

5.その他

「その他」なんてくくってしまいましたが、要は「開業に必要なもの」です。例えば名刺とか印鑑とか必要でしょうし、あと文房具とか事務用品とか、人によっては業務専用のスマホ、それに専用の有料アプリも必要だったりしますので、それらひっくるめてって感じです。

ちなみに(僕はやっていませんが)20万以上の開業費は減価償却みたいな感じで「毎年分割で経費にする」事も出来るみたいです。

※ただし、商品の仕入れ代金や10万以上の機器、機材は「開業費」には出来ません(「資産」になる)。要は、10万以上のPCは「開業費」として一括経費にする、なんていうことは出来ないわけです。ちょっと残念(?)

※僕も勘違いしてたのですが、開業届の「開業日」前にかかった費用も「開業費」やその他諸々の「経費」に出来ます。なので、退職した翌日からのレシートや領収書はしっかり保存しておくことをオススメします。

 

とりあえずこんなところですか。

僕もまだまだ駆け出しなので、これから気になった事はいろいろ書いていこうと思います。

フリーになる時に必要な手続き

さて、一応本格的にフリーランスとしてスタートを切りました。(と言ってもまだ1週間なので、何とも言えませんが)
ただ今のところは、前みたいな人間関係のトラブルはなく、むしろ人間関係は良い感じです。

さて、僕も開業前には色々調べたり手続きをしましたが、フリーになる時に必要な手続きって何だろう?って思ったので、簡単にまとめてみました。
勘違いがあるかもしれませんが、参考程度に。


1.開業届の提出
まあ、まずはこれをしないと始まりません。
というか、とりあえずこれだけ提出しておけば、法律的にはフリー(というか、事業主)と認められます。

そうそう、「屋号」は無くても良いので「いい屋号が浮かばない…」っていう場合は、とりあえず屋号なしでもOKです。(ブランディングとか認知度のことを考えたら、あるに越したことはないですが。)
あと「業種」ですが、これ一つだけで個人事業税が課税されるかどうかや税率が変わるので、複数の業務を行う際は慎重に選択しましょう。(もちろん、ウソを付くのはダメですが。)

2.青色申告承認申請書の提出
フリーランス」でググると、大抵出てくる言葉ですよね。簡単に言えば「白色申告よりも詳細に申告するから、控除額(=非課税額)を65万円増やして下さい」という申請用紙です。
これ、始めは「記帳が大変そう…」とか「複式簿記って難しそう…」って思いましたが、実際にやり始めてみて分かりました。

これは絶対に申請した方がいいです。

いや、やってみて分かったんですが、複式簿記とか仕訳って、思ったほど難しくないです。
もちろん自営で店舗などを経営する人はすごい複雑なんでしょうが、僕のような常駐型フリーエンジニアの場合、基本的に

  • 収入…毎月の契約金
  • 支出(経費)…通勤代&車・ガソリン代

くらいで、あとはほとんど文房具とか技術関連の書籍とか、細々とした経費くらいでしょうし、それなら一度仕訳パターンを覚えてしまえば、1日10数分で記帳が完了すると思います。
それに最近は「freee」とか「MFクラウド確定申告」のような、安くて非常に便利な会計ソフトもたくさんありますので、知識が乏しくても何とかなるでしょうし、それで控除額が65万円増えるなら、絶対そのほうが良いと思います。

3.国民健康保険国民年金
会社員と違い、フリーだと会社の社会保険や厚生年金には入れないので、国民健康保険国民年金に加入することになります。(ただし退職後2年までなら前の会社のものに継続して加入出来ます。)

ただ、これが結構な額なんですよ…会社員って優遇されていたんだな、って実感します。(会社員は会社が半額負担してくれるから)
まあ一応、国民健康保険は「親などの扶養に入る」、国民年金は「減免申請をする」事で、減免される可能性はあります。
※ただ、普通は「開業する=満足に働ける」状態だと思うので、難しいとは思います。逆に病気や怪我などで「働き無くても働けない」人は、絶対に申請すべきだと思います。

…ちょっと長くなりそうなので、次回はまた後日。

フリーエンジニアになりました その2

また日付が空いてしまいました。すいません。

色々あって、なかなかパソコンに触れませんでした…

 

で、前回の続きなんですが、ニコニコ動画を見ていたら、こんな記事が。

news.nicovideo.jp

もちろん僕は学生ではないんですが、心当たりが無いわけではないんです。

実は闘病中、医師の勧めで発達障害とかアスペルガーに関する検査を受けたことはあるんですが、結果がかなり少数派というか、人とはかなり違っていて、それ以降、医師やカウンセラーの言葉に「個性だから」という言葉が明らかに増えたような気がします。(ただ僕の場合、コミュ障なんてことはなく、むしろ「よく喋るエンジニア」と言われることがほとんどですが…)

 

ただ、その「個性」ってのが理解されなくて、苦労した事は決して少なくないです。間違いなく求められた以上の成果を残しているのに、無理やり理由をつけて色々言われたり、最悪クビにされたり。

でも自分を認めてくれた会社では高い評価も頂いたし、自分も頑張れたので、これまで何とか会社員を続けてきたのですが、今回の件で完全に「会社は社員なんか守ってくれない」って思ったわけです。それで完全にフリーになる決心がつきました。「会社に依存する生き方は自分には無理だ!」って。

 

ただ先程「フリーになる決心」と書いたのですが、前回も書いた通り「フリーになってでも働いて食べていかなきゃいけない」のもまた事実なわけで。

こういう「(コミュ障など)病気や個性の件でうまく働けない」などのネットニュースを見ると大抵「だからニートなんだよ」とか「人生詰んだ」とか書いてあるけど、それは全くのお門違い。大変だろうし、つらいのも分かるけど、だからって「働かない理由」にはならないし、学齢期を過ぎた以上、普通は何かしら働いて収入を得ないといけないわけで(もちろん、医者が終了禁止するレベルの病気などは別として。)。

 

実際僕も生きづらさを感じてはいるけど、絶対そんな事は言いたくない。むしろ働き盛りの30代にほとんど働けなかった分を今から取り戻したい、という気持ちが非常に強い。間違いなく闘病中だった10年前より、気持ちはむしろ若いと思う。

でも、残念ながら、世間というのはそんなに甘くないし、病気が治ったからって「8年間の病気」の事がチャラになるわけじゃない。でも何とかして働いて食べていかないといけない。これは紛れもない現実。

じゃあどうするか、といったら「そんな事が関係ない方法で生きていけばいいだけじゃないか」という結果に至ったわけです。まあ、そうしないと食べていけないわけですし。「こち亀」の両さんの言葉を借りれば「生きるか死ぬかではなく、どう生きるか」ってやつです。

 

なんかフリーエンジニアになったってことを軽く書くつもりが、やけに重い内容になってしまいましたね…次はもっと軽い感じの事を書こうと思います。

フリーエンジニアになりました。その1

前回から、また期間が空いてしまいました…すいません。

前回の記事で会社を辞めたと書きましたが、新しい仕事探しやそれに伴う転居やらで、なかなか時間が取れませんでした。
で、新しい仕事ですが、いろいろ考えた結果、しばらくの間フリーエンジニアとしてやっていくことにしました。

ただ「フリーエンジニア」と言うと、ネット上ではよく「報酬が高い」とか「スキルを活かして起業して」とかよく書かれていますが、そのつもりは(今のところ)全くありません。
むしろ「そのほうが自分に合っている(自分には会社員は合わない)かも」という思いが強かったので、一旦フリーを経験するのも悪く無いかな、と思いまして。

というのも、前の会社の事は何回か書いたのですが、実はこれが初めてではなく、過去にも何回かあったんです。
「自分の事を理解してくれない(理解しようとしない)」とか、ちょっと個性的なだけで「異端児」「反組織的」扱いとか。

確かに僕は自分でも「個性的」とか「ちょっと人と考え方が変わってるかも…」という所があるのは認めていますが、それだけでそういう扱いされて、ロクな評価をされないというのは、ちょっとねえ。
(それに、自分を認めてくれた会社ではちゃんと結果を残し続けてきた、という確固たる実績と自負が自分にはありますから。)

あともう一つ、約10年のうつ病の経歴を理由に敬遠されることが、本当に多いです。
「スキル的には、ぜひ来て欲しいんですが…」とは言うんですが、悪い言い方をすれば「契約社員でなら雇ってやってもいいよ」みたいな、足元を見る会社の多いこと、多いこと。
「再チャレンジ」ってなんだったんだろうなあ…って感じです。

まあ、そんなことが本格的にうつ病を発症して以来、8年間近く続きまして、もちろん今後も続くと考えています。
そうなると、普通に考えたら、まともに働く事自体、困難で苦痛な訳です。
でもそんなこと言っても、働かない訳にはいかないし、そもそも働かないと食べて行けないわけです。
「働かざる者食うべからず」ですから。

それに、何だかんだ理由をつけて働こうとしないニートになんか絶対なりたくないですし、やっと普通に働けるようになった以上、まともに働けなかったこの約10年の分も働きたい、という思いがあります。
それになりより、何だかんだ言って、やっぱり自分はITエンジニアの仕事が天職だと思ってますから。

そう考えた時、「組織で働けないなら、フリーエンジニアとして働くのも、悪くないんじゃない?」って思ったんです。
本当、ITエンジニアで(手に職を付けてて)良かった、って思いました。

もちろん、実際はこんな軽いノリの話ではなく、むしろ「フリーエンジニアしか食べていく方法がないから」と言ったほう正解なのですが、
まあ、フリーになったのはこういう経緯があったからなのです。

この事については、もうちょっと書きたいので、次回に続きます。

JavaScriptで動的なバリデーション表示

オレは会社をやめたぞ!ジョジョーーッ!

という訳で、今まで色々書いてきた会社のことですが、とうとう辞めました。
今まで色々あったのですが、「こんな奴の下にずっといたら、絶対ダメになるな…」という決定的なことがあったので、案の定「辞めてもらってもいいんだよ」と言われた時に「はい、じゃあ辞めます。」って言いました。
向こうはまさか辞めるとは思ってなかったようですが、そんな事知ったことじゃないし、それにDirectXの件は要望通りのものは一通り完成させましたし(このブログでは、小出しにしてますけどね…)
C#VC++、両方分かる人が僕しかいない会社でしたが、あれはあの後どうなるんだろうな…


で、今週は訳あって、依頼された簡単な問い合わせシステムをPHPで作成してたのですが、今までWebシステムの入力検証って、定番通りサーバーで行ってエラー内容をヘッダとかに表示させてました。
ただ、最近は色々なWebサイトで見られる通り、クライアント側で行って動的にHTMLなどを作成して…というのが多いですよね。

例えば、
f:id:Makky12:20160826202128p:plain
エラーがない場合は、こんな感じなんですが、


f:id:Makky12:20160826202137p:plain
入力内容がエラーだと、HTMLなどを動的に作成して、こんな風にわかりやすく表示してくれるやつです。

もともと「どうやって作ってるんだろうな」と思っていましたが、いい機会だったので、自分で作ってみました。
(もっと良いやり方もあるのでしょうが、時間の関係もあり、とりあえず今回はこんな感じです。)

<!-- HTML側のコード(一部抜粋) -->
<tr>
    <th>
        お名前<span class="must">必須</span>
    </th>
    <td class="td" id="td_name">
        <input type="text" class="form-control" name="name" id="form_name"
        value="" maxlength="10" placeholder=" 例)山田 太郎" 
        onchange="validate('td_name', 'form_name');">
    </td>
</tr>|


// JavaScript側のコード
function validate(objTd, objId)
{
    var objCtrl = document.getElementById(objId);
    var objRegExp = null;
    var msg = "";

    switch(objId)
    {
        // 本当は他にもあるけど、今回は省略。
        case 'form_name':

            if("" == objCtrl.value) {
                msg = "お名前は必須です。";
            }else{
                if (objCtrl.value.length > 10) {
                    msg = "お名前は10文字以内で入力して下さい。";
                }else{

                    // 正規表現による文字チェック
                    objRegExp = new RegExp(/^[\sa-zA-Z0-9!#$%&=-~^@+.ぁ-んーァ-ヶー一-龠1-9A-Za-z!#$%&=-~^@+.、。?-]+$/);
                    if (!objRegExp.test(objCtrl.value)) {
                        msg = "お名前の値が不正です。";
                    }
                }
            }

            break;
    }

    displayErrorInfo(objTd, objId, msg);
}

function displayErrorInfo(objTd,objId, msg){

    var objParent = document.getElementById(objTd);
    var objCtrl = document.getElementById(objId);

    // objIdの文字を末尾に追加しているのは、他のコントロールで使用した際IDがかぶるのを防ぐため。
    var objChild = document.getElementById('form_childDiv' + objId);

    // 同じエラーメッセージが何個も表示されないように、既存のエラーメッセージは削除する。
    if(null != objChild)
    {
        objParent.removeChild(objChild);
    }

    if("" != msg) {
        // エラーが有る場合
        objCtrl.style.backgroundColor = "#FADBDA";

        var errorNode = document.createElement("div");
        errorNode.id = "form_childDiv" + objId;
        errorNode.style.color = "#FF0000";
        errorNode.innerText = msg;
        objParent.appendChild(errorNode);
    }else{
        // エラーがない場合
        objCtrl.style.backgroundColor = "#FFFFFF";
    }
}

最初は難しそうだな、と思っていたけど、appendChild()の仕組みを理解すれば、表示の変更自体はそこまで難しくなかった。
ていうか、むしろ正規表現が厄介だった…
RegExp()の引数をクオーテーションで囲む必要がない、ってことになかなか気づかなかった…(Zendの「Zend_Validate_Regex」だと、囲む必要があったはずだけど…)

まあ、この辺は要調査ですね。