20211129のHTMLに関する記事は7件です。

Electronの始め方〜HelloWorldまで〜

普段はElectronとは無縁の生活の者です。 2018年くらいの記事を読んで失敗したので書き留めます。 今回はElectronでhello worldできれば完成です! ディレクトリ作成 mkdir helloworld ファイルの追加 次にhelloworldディレクトリに3つファイルを追加します。 helloworld/  ├ main.js  ├ preload.js  └ index.html main.js const { app, BrowserWindow } = require('electron') const path = require('path') function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } app.whenReady().then(() => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) preload.js window.addEventListener('DOMContentLoaded', () => { const replaceText = (selector, text) => { const element = document.getElementById(selector) if (element) element.innerText = text } for (const type of ['chrome', 'node', 'electron']) { replaceText(`${type}-version`, process.versions[type]) } }) index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> </head> <body> <h1>Hello World!</h1> </body> npmのインストール cd helloworld npm init package.jsonを編集します。 package.json { "name": "helloworld", "version": "1.0.0", "description": "", "main": "main.js", "dependencies": { "electron": "^16.0.2" }, "devDependencies": {}, "scripts": { "start": "electron .", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ここで注意する点は、 "main"の部分が"main.js"になっているか。 それと、 "script"の部分に"start"の部分を足すこと。 次に、やっとelectronのインストールに入ります。 npm install --save-dev electron helloworldディレクトリでインストールします。当たり前だけど、間違えがち。変なディレクトリでインストールしがち。 これでOK 起動します。もちろんhelloworldディレクトリで。 npm start めでたくhelloworld!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SVGのパスについて詳しく調べてみる(SVG.js)

SVGのパス関係の知識が必要になってきたので仕様を確認していきます。本記事では直接HTMLなどでは書かずにSVG.jsを使っていきます(ただしパスの仕様自体はHTMLでやってもSVG.jsを使っても変わりません)。 以下のMDNやsvg.jsのドキュメントを見ていきます。 使うもの SVG.js v3.1.1 JSFiddle (例 : https://jsfiddle.net/yar6x4hk/202/ ) パスの描画の基本 表示の確認のためにまずはパスの確認用の描画のインスタンスの生成処理について触れていきます。パスを描画するためにはpathメソッドを使います。引数に各パス設定の文字列を指定します。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 Q 100 20 150 100 T 250 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); パスの位置を移動させるM パスの位置を移動させるにはM <X座標> <Y座標>といったように指定します。例えばX=50, Y=100の位置に移動させたければM 50 100といったように書きます。Mはmove toのMのようです。 パスの位置が変更になるだけでこの記述だけでは表示上には影響しません(これだけだと何も表示されません)。 直線を描画するL 指定位置に向かって直線を描画したい場合にはL <X座標> <Y座標>といったように指定します。例えばX=50, y=50の位置からX=150, Y=50の位置に直線を描画したい場合にはM 50 50 L 100 50といったように書きます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 50 L 150 50'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); パスの指定はどんどん繋げていくことができる パスの指定はどんどん繋げていくことができます。例えば以下のように2つ目のLを指定すれば折れ線となります。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 50 L 150 50 L 150 150'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); Mを複数入れて複数の線を描画する・・・といったこともできます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 50 L 150 50 M 50 150 L 150 150'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); 水平方向に対する直線を描画するH H <X座標>とすることで水平方向に対する直線を描画することができます。Lと似たような感じとなりますがY座標の指定が要らなくなるため水平方向の直線を描きたい場合にはこちらの方がシンプルです。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('m 50 50 H 150'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); 垂直方向に対する直線を描画するV V <Y座標>とすると垂直方向に対する直線を描画することができます。Hと対になる設定となります。こちらも水平方向の座標を変えない場合にはLよりも記述がシンプルになります。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('m 50 50 V 150'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); 終点と始点を繋げるZ パスの最後にZと終点と始点のパスを繋げることができます。Lなどで始点の座標を指定するのと同じ挙動になりますがこちらの方がシンプルですし始点の座標を変えた際などにも楽です。 以下の例ではX=150, Y=150の位置からZの記述によってX=50, Y=50の始点に線が繋がれています。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('m 50 50 H 150 V 150 Z'); path.attr({stroke: '#333', 'stroke-width': 5, fill: '#fff'}); パスの塗りを透明にする設定 デフォルトだとパスの内部は塗り設定がされます(フォトショやイラレなどでお馴染みな感じの挙動になります)。以下のようにfill設定をしていないと黒く塗られます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('m 50 50 H 150 V 150 Z'); path.attr({stroke: '#333', 'stroke-width': 5}); fillにnullや空文字などを指定しても塗りが消えず、透明度設定をすると線も消える・・・感じだったためここまでのコードサンプルでは線のみが見えやすくなるように背景色と同じ白色を塗りに設定していましたが、MDNの資料を読んでいたらどうやらtransparentとfillに設定することで塗りだけ透明にして線のみを表示することができるようです。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('m 50 50 H 150 V 150 Z'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 小文字による想定値指定 LやH、Vなどの記号は小文字にすると座標の指定が相対値による指定となります(前節までで触れてきた大文字による指定は絶対値による座標の指定です)。 例えばh 50とすれば今の座標から50加えたX座標に線を引く形となり、h -50といったように負の座標を指定すれば今の座標から50左側に移動したX座標の位置に線を引く形となります。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 50 h 50 v 50 h -50'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 3次ベジェ曲線の指定 フォトショやイラレなどのAdobeソフト、もしくは3Dなどのソフトを使われている方にはお馴染みのベジェ曲線について触れていきます。制御点の指定を2つ必要とする3次ベジェ曲線と1つのみ必要とする2次ベジェ曲線の2つがありますが、まずは3次ベジェ曲線について触れていきます。 記法としてはC <1つ目の制御点のX> <1つ目の制御点のY> <2つ目の制御点のX> <2つ目の制御点のY> <曲線の末端のX> <曲線の末端のY>といった具合にXとY座標がそれぞれ3つずつ必要になります。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 C 50 50 100 50 100 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 分かりやすいように各座標に円を追加してみます。先頭のXとYの指定(1つ目の制御点)がシアン、真ん中のXとYの指定(2つ目の制御点)がマゼンタ、3つ目のXとYの指定(曲線の末端)を緑に設定しています。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 C 50 50 100 50 100 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); var circle1 = draw.circle(10); circle1.x(50 - 5); circle1.y(50 - 5); circle1.fill('#0af'); var circle2 = draw.circle(10); circle2.x(100 - 5); circle2.y(50 - 5); circle2.fill('#f0a'); var circle3 = draw.circle(10); circle3.x(100 - 5); circle3.y(100 - 5); circle3.fill('#0fa'); 他と同様に小文字のcを使うことで相対座標で指定することができます。注意点として基準点は全て始点となります。2つ目の制御点は曲線の末端を基準にしたりはしないようです。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 c 0 -50 50 -50 50 0'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 3次ベジェ曲線の曲線を滑らかに繋ぐS 滑らかに曲線を繋ぎたい場合はSを使います。この場合1つ目の制御座標は前の曲線のものの対向(反転させた位置)のものが使われるため指定が不要になります(対向の座標の制御点が使われるためスムーズな曲線を表現できます)。つまり制御点のXとYの指定が1つのみとなります(イラレのペンツールなどでもベジェ曲線で片方を伸ばしたら対向の制御点も同じ感じで伸びたりしますがそれに近い感じの挙動になります)。S <制御点X> <制御点Y> <曲線の末端のX> <曲線の末端のY>といった形で指定します。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 C 50 50 100 50 100 100 S 150 150 150 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 以下のコードではSによる制御点の指定箇所をシアン、曲線の末端の座標の指定箇所をマゼンタの円で表示しています。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 C 50 50 100 50 100 100 S 150 150 150 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); var circle1 = draw.circle(10); circle1.x(150 - 5); circle1.y(150 - 5); circle1.fill('#0af'); var circle2 = draw.circle(10); circle2.x(150 - 5); circle2.y(100 - 5); circle2.fill('#f0a'); 他と同様に小文字のsを使うことで始点の座標からの相対座標で指定していくことも可能です。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 C 50 50 100 50 100 100 s 50 50 50 0'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 2次ベジェ曲線の指定 2次ベジェ曲線では制御点は1つのみ使います。細かい制御が効きづらくなる一方で記述がシンプルになるため、2次ベジェ曲線で事足りる場合などに便利です。 記法としてはQ <制御点のX> <制御点のY> <曲線の末端のX> <曲線の末端のY>といったように指定します。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 Q 100 20 150 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 以下の記述では設定している制御点をシアン、曲線の末端の座標をマゼンタの円で表示しています。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 Q 100 20 150 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); var circle1 = draw.circle(10); circle1.x(100 - 5); circle1.y(20 - 5); circle1.fill('#0af'); var circle2 = draw.circle(10); circle2.x(150 - 5); circle2.y(100 - 5); circle2.fill('#f0a'); 他と同様にqの小文字で始点からの相対値で座標を指定することができます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 q 50 -80 100 0'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 2次ベジェ曲線の曲線を滑らかに繋ぐT 3次ベジェ曲線と同様に2次ベジェ曲線でも曲線を滑らかに繋ぐためのTの指定が存在します。こちらは制御点の指定は無くなります(直前の制御点の対向の位置が使われる形になります)。T <曲線の末端のX> <曲線の末端のY>といったように書きます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 Q 100 20 150 100 T 250 100'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 他と同様にtの小文字で始点からの相対値で末端の座標を指定することができます。 var draw = SVG().addTo('body').size(600, 600); var path = draw.path('M 50 100 Q 100 20 150 100 t 100 0'); path.attr({stroke: '#333', 'stroke-width': 5, fill: 'transparent'}); 曲線に円弧を使うAもあるものの・・・ 他にも円弧を使う形のAの指定もあります。が、軽く触って見た感じ結構設定が直観的とは言い難く個人的に使う機会が少なそう・・・という所感を受けたため今回は触れずに終わります。将来必要になってきたらまた深堀りしようと思います。 参考文献・参考サイトまとめ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デイトラ中級 DAY30-32 中級総復習編 googleフォームにおけるname要素の探し方

はじめに 初めての投稿のため、至らない点があるかと思いますがご容赦ください。 筆者はプログラミング歴3ヶ月ほどの初心者で、現在はデイトラを学習中です。 デイトラ中級 DAY30~32:中級総復習編におけるgoogleフォームのカスタマイズについて、動画やTipsの説明だけではわかりにくいと感じたため、本記事を作成しました。 googleフォームでフォームを作成する これはデイトラ内の動画や参考記事の通りで問題ありません。 ただし、googleフォームのページでブラウザの検証機能を使用する際は、必ずプレビュー画面から検証を行う。 フォームの編集画面から赤丸で示したボタンをクリックするとプレビュー画面が開く。 (編集画面ではURLの末尾が /edit になっている) (プレビュー画面ではURLの末尾が /viewform になっている) 検証機能を使用してaction属性とname属性を探す 作成したフォームのプレビュー画面でブラウザの検証機能を使用したら、Elementsタブで Ctrl + F (macの場合は command + F)を押し、検索欄を開く。 「<form」で検索する。 画像のような記述を見つけることができる。 画像の赤線で示したformタグ内のaction属性を、自身が組み込みたいhtmlファイルのformタグのaction属性にコピペする。 次に、「お名前」や「お問い合わせ内容」など、フォーム作成時に設定した質問のタイトルで検索する。 画像のような記述を見つけることができる。※メールアドレスのname属性は見つからなくても問題ありません! 画像の赤線で示した箇所の数字を、自身が組み込みたいhtmlファイルのinputタグやtextareaタグのname属性に「name="entry.数字"」となるようにコピペする。 メールアドレスのname属性について 結論だけを言うと、下記の通りの記述でメールアドレスをgoogleフォームにPOSTできます。 <input type="email" name="emailAddress"> ブラウザの検証機能でPOSTされたデータの中身の確認ができ、メールアドレスのname属性を見つけることができます。 詳細は下記のリンクからどうぞ。 【2020年最新版】カスタマイズしたフォームからJavaScriptでGoogleフォームに回答を送信 - Qiita ChromeデべロッパーツールでPOSTされたパラメータの中身を確認する あとがき いつの間にかgoogleフォームの仕様が変わっていたようで、 デイトラの解説の通りにコンタクトフォームを実装できず、つまずいていました。 上記リンクの記事のおかげで、無事解決することができました。 また、ブラウザの検証機能の使い方の良い勉強になりました。 (自力で調べたけど、デイトラのメンターさんに質問すればよかったのかも) 同じところでつまずいているデイトラ民の助けになれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マリリン・マンソン・ゲーム ~本物の「マリリン・マンソン」を探せ!~

事の顛末 以前,Twitter にてこんなつぶやきを発見しました. 要するに,外国人の日本語学習者に向けて, 「カタカナの『リ』『ソ』『ン』,ちゃんと区別できる?」 「できなかったらもっと練習だぜ!」 「ところで,“Marilyn Manson”ってカタカナでは『マリリン・マンソン』って書くんだぜ!」 というつぶやきです. そこで 0.2kg 君は思いました. 「なるほど,マリリン・マンソンやべぇ」 「この 3 つの読み分け・書き分けは日本語ノンネイティブにはやはり鬼門なのか」 というわけで, 「マリリン・マンソン」を使って「リ」「ソ」「ン」の区別を練習できるゲームを作っちゃおう! という発想から,本記事でご紹介する「マリリン・マンソン・ゲーム」の作成に至りました. これが「マリリン・マンソン・ゲーム」だ! ここでプレイ可能です. ルール 10 秒以内に 25 個の「マリリン・マンソン」とその偽物から,本物の「マリリン・マンソン」を探し当てる. 得点は「(残り時間 [sec])*100-(ミス回数)*10」で計算される. 所感 残り時間で難易度調整ができそうですが,筆者も含めた日本語ネイティブにとっては 10 秒ちょいくらいがクリアできるかできないかのギリギリなんじゃないかと思っています. その後 個人的にノンネイティブの日本語学習者さんの反応が気になったので,知人の外国人留学生 2 人にこのゲームをやっていただきました.その結果, 「選択肢を 2, 3 個読むだけで制限時間終わっちゃった……」 「僕もだよ.制限時間までに 3 つより多くは読めなかったよ.これってスピード勝負のゲームでカタカナの読み分けができてるかのテストじゃない気がする」 という非常に興味深いコメントをいただきました. 「マリリン・マンソン・ゲーム」,どうやら日本語学習にはあまり役に立たないゲームのようです. どっとはらい.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

5行のHTMLで15パズル作ってみた(追記:3行まで短縮)

概要 HTMLとJavaScriptを用い,「15パズルを実装する」というお題でコードゴルフ(ショートコーディング)をしてみました. 15パズルとは 15パズルは、スライディングブロックパズル(Sliding puzzle)のひとつである。4×4のボードの上に4×4-1すなわち15枚の駒があり、1駒ぶんの空きを利用して駒をスライドし、駒を目的の配置にする。(Wikipediaより) 要するに数字を動かして順番にそろえるコレです. レギュレーション 1行79文字 全角文字は1つにつき2文字とカウントする 基本的には,2ちゃんねるで行われていた「7行プログラミング」に準ずる形で設定しています. 遊び方 キーボードの矢印キーで,空きマスに隣接している数字を指定した方向に動かします. 数字の配置が のようになればゲームクリア. 実行環境 言語 : HTML, JavaScript 動作ブラウザ : Google Chrome, Microsoft Edge 他ブラウザでの動作確認はぶっちゃけめんどくさくてやっておりません.情報求む. 完成コード <body id=D onKeyDown=F(event.which-37) onLoad='A=[];g=f=0;for(i=16;i--;A.push(i)) ;s="①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ ";F=(K)=>{if(!f&&0<=K&&K<4){a=A.indexOf(15);[ M,N]=K%2?[--K?a<12:a>3,a+~-K*4]:[(a+!!K)%4,a+K-1];M&&([A[a],A[N]]=[A[N],15]);S="" ;for(k=16;k--;S+=s[A[k]]+(k%4?"":"<br>"));if(g)for(f=1,r=16;--r;)f&=(A[r-1]>A[r]) }D.innerHTML=S+(f?"Win":"")};for(j=999;j--;)F(4*Math.random()|0);g=1'> 418バイト.スクリプト本体は全てonLoad属性にぶっこんでいます. 解説 スクリプト本体をonLoad属性から出して,コードに適宜改行,スペースとコメントなどを挟んだものがこちらになります. <body id=D onKeyDown=F(event.which-37)><script> // A : 盤面配列 0~15の数値を格納 // 0~14 が丸数字 ①~⑮ に対応,15 は空きマス A = []; // g : ゲーム中か(初期化中でないか)否かのフラグ // f : ゲームクリア判定のフラグ g = f = 0; for(i = 16; i--; A.push(i)); // 盤面 A に 0~15 の数値を逆順で格納 s = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ "; // s : 盤面表示用丸数字テキスト // F : キー入力を受け取り,盤面を更新し,ブラウザに描写する関数 F = (K) => { if(!f && 0 <= K && K < 4){ // 未クリア & 入力されたキーが矢印キーならば a = A.indexOf(15); // a : 空きマスの位置 // M : 入力した方向に数字が動かせるかのフラグ // N : 動く対象の数字の位置 [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; M && ([A[a], A[N]] = [A[N], 15]); // 空きマスと数字の位置を交換 S = ""; // S : ブラウザに表示するテキスト 盤面その他を格納 for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); // 盤面を格納 if(g){ // ゲーム中ならば for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); // クリア判定を行い,f を更新 } } } // ブラウザに盤面を表示,クリアしたら "Win" の文字列も追加 D.innerHTML = S + (f ? "Win" : ""); } for(j = 999; j--;) F(4*Math.random() | 0); // 盤面初期化 g = 1; // 初期化終了,g を更新(フラグをゲーム中に設定) </script> 上から順番に見ていきましょう. <body id=D onKeyDown=F(event.which-37)> ブラウザ内でキーが押下されたとき(onKeyDown),押下されたキーのキーコード(event.which)から37を引いたものを取得して,関数Fに投げています. 矢印キーの←,↑,→,↓のキーコードがそれぞれ 37, 38, 39, 40 なので,37 を引くことで 0, 1, 2, 3 に変換し,扱いやすさ向上&コード短縮に役立てています. g = f = 0; gはゲームの最中であるか(盤面の初期化の最中ではないか)を示すフラグ,fはゲームをクリア済かを示すフラグです. どちらも0すなわちfalseで初期化しています. for(i = 16; i--; A.push(i)); 盤面配列Aに 0 から 15 までの整数を逆順で格納しています. このfor文の表記ですが,変数iの値を 16 で初期化した後に条件式内でiのデクリメントを行っています.これは,i=16から条件式がfalse,すなわちi=0になるまでiを減算し続ける,という実装であり, for(i = 15; i >= 0; i--) A.push(i); という実装と同様の動作をします. 次に,関数F(K)の内部を見ていきましょう. if(!f && 0 <= K && K < 4){ ... 先述の通り,fはゲームクリア済みか否かを示すフラグであり,0ならば未クリア,0以外ならばクリア済みであることを表します. 関数Fの引数であるKは先述した (キーコード-37) の値ですから,このif文は,ゲームが未クリア,かつ入力が矢印キーからならば(i.e. Kが 0, 1, 2, 3 のいずれかならば)実行されます. a = A.indexOf(15); [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; ここで出てくる変数の意味は以下の通り. a : 盤面中の空きマスのある位置(0 ~ 15 の整数) M : 矢印キー押下時に,指定された方向への数字の移動が可能かどうかのフラグ(0で移動不可能,0以外で移動可能) N : 移動可能な場合,移動する対象の数字がある場所(0 ~ 15 の整数) M, Nへの代入部は複雑なので,if文で書き換えてみます. if(K % 2){ if(--K) M = (a < 12); else M = (a > 3); N = a + ~-K * 4; }else{ M = (a + !!K) % 4; N = a + K - 1; } if文は,押下された矢印キーに応じて次のように動作します. 上下キー(K=1 or 3) : 1 -> true -> 前半ブロック実行 左右キー(K=0 or 2) : 0 -> false -> 後半ブロック実行 キー入力が上下キーの場合,さらに--Kの値,すなわち上下のどちらが入力されたかが評価され,Mの値は次のように設定されます. 上キー(K=3) : --K=2 -> true -> M = (a < 12); 空きマスの位置が最下行以外 (a < 12) のとき,数字をより上行へ移動可能 下キー(K=1) : --K=0 -> false -> M = (a > 3); 空きマスの位置最上行以外 (a > 3) のとき,数字をより下行へ移動可能 Nへの代入時における~-KはK-1と同値であり,演算子~, -の優先度が*よりも上であることから,コード短縮テクニックとして頻繁に使用されます(K+1と同値である-~Kも同様). また,Kの値は先程デクリメントした値からさらに -1 されることから,Nに代入される値は以下の通りになります. 上キー('K'=3) : ~-K=1 -> N = a+4 (空きマスの 1 行下にある数字が移動) 下キー(K=1) : ~-K=-1 -> N = a-4 (空きマスの 1 行上にある数字が移動) キー入力が左右の時は以下の通り. 右キー(K=2) : !!K=true -> !!K=1 -> M = (a+1)%4, N = a+1 空きマスが左端以外の列にあるとき,空きマスの 1 列左にある数字が移動 左キー(K=0) : !!K=false -> !!K=0 -> M = a%4, N = a-1 空きマスが右端以外の列にあるとき,空きマスの 1 列右にある数字が移動 ここでは,否定演算子!を 2 回重ねて使うことで,0ならば0,それ以外ならば1という変換を行っています. S = ""; for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); ここは盤面のテキスト化です.ここでも先述の短縮forループを利用しています. 盤面Aのk番目の要素について,それに対応する丸数字をとってきてSに加算しています. kが 4 で割り切れる,すなわち行末であるときに改行も加算します. if(g){ for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); } } ゲームの最中(g==true)であれば,if文内部でクリア判定を行い,フラグfを更新します. ここでは,いったんfを1(クリア済)で初期化した後,盤面配列Aの要素が昇順で並んでいれば,ループが終わったときにもfがtrueに保たれる(otherwise false),という実装にしています. ここでも先程の短縮forループを利用していますが,条件式のデクリメントが前置されているのは,rが 0 のときにループを抜けてほしいため(r-1に負になってほしくない)です. D.innerHTML = S + (f ? "Win" : ""); 盤面をテキスト化した文字列Sをブラウザに出力します. クリアしていれば(i.e. f==true)“Win”の文字列も一緒に出力します. これで関数F(K)の定義は終了です. for(j = 999; j--;) F(4*Math.random() | 0); g = 1; 最後に,ゲームの初期化処理を行っています. Fに 0, 1, 2, 3 のいずれかの数値を引数として代入して実行する処理を 999 回繰り返しています. すなわち,「とりあえずランダムな方向に数字を(最大)999 回動かす」という処理を行っています. 先程クリア判定のためにフラグgを用意しているのは,この盤面の初期化時にも関数Fを利用しているからであり,この時点でクリア判定が行われてしまうのを防ぐという目的があります. そして,初期化後にgをtrueに設定して,ゲーム中フラグを立てます. 最後に とりあえず 5 行に収めることには成功しましたが,もっとスマートな実装ができないものかという模索は今後も気が向いたときにやっていきたいなと思う所存であります. コードゴルフ楽しい! 追記 Qiita の皆様の叡智をお借りして,3 行半以下まで短縮されました(コードと更新履歴はコメント欄を参照). 心より感謝申し上げます. 追々記 またもや皆様の叡智をお借りして,ついに 3 行以内に収まりました(コメント欄参照). 本当にありがとうございます.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

5行のHTMLで15パズル作ってみた

概要 HTMLとJavaScriptを用い,「15パズルを実装する」というお題でコードゴルフ(ショートコーディング)をしてみました. 15パズルとは 15パズルは、スライディングブロックパズル(Sliding puzzle)のひとつである。4×4のボードの上に4×4-1すなわち15枚の駒があり、1駒ぶんの空きを利用して駒をスライドし、駒を目的の配置にする。(Wikipediaより) 要するに数字を動かして順番にそろえるコレです. レギュレーション 1行79文字 全角文字は1つにつき2文字とカウントする 基本的には,2ちゃんねるで行われていた「7行プログラミング」に準ずる形で設定しています. 遊び方 キーボードの矢印キーで,空きマスに隣接している数字を指定した方向に動かします. 数字の配置が のようになればゲームクリア. 実行環境 言語 : HTML, JavaScript 動作ブラウザ : Google Chrome, Microsoft Edge 他ブラウザでの動作確認はぶっちゃけめんどくさくてやっておりません.情報求む. 完成コード <body id=D onKeyDown=F(event.which-37) onLoad='A=[];g=f=0;for(i=16;i--;A.push(i)) ;s="①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ ";F=(K)=>{if(!f&&0<=K&&K<4){a=A.indexOf(15);[ M,N]=K%2?[--K?a<12:a>3,a+~-K*4]:[(a+!!K)%4,a+K-1];M&&([A[a],A[N]]=[A[N],15]);S="" ;for(k=16;k--;S+=s[A[k]]+(k%4?"":"<br>"));if(g)for(f=1,r=16;--r;)f&=(A[r-1]>A[r]) }D.innerHTML=S+(f?"Win":"")};for(j=999;j--;)F(4*Math.random()|0);g=1'> 418バイト.スクリプト本体は全てonLoad属性にぶっこんでいます. 解説 スクリプト本体をonLoad属性から出して,コードに適宜改行,スペースとコメントなどを挟んだものがこちらになります. <body id=D onKeyDown=F(event.which-37)><script> // A : 盤面配列 0~15の数値を格納 // 0~14 が丸数字 ①~⑮ に対応,15 は空きマス A = []; // g : ゲーム中か(初期化中でないか)否かのフラグ // f : ゲームクリア判定のフラグ g = f = 0; for(i = 16; i--; A.push(i)); // 盤面 A に 0~15 の数値を逆順で格納 s = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ "; // s : 盤面表示用丸数字テキスト // F : キー入力を受け取り,盤面を更新し,ブラウザに描写する関数 F = (K) => { if(!f && 0 <= K && K < 4){ // 未クリア & 入力されたキーが矢印キーならば a = A.indexOf(15); // a : 空きマスの位置 // M : 入力した方向に数字が動かせるかのフラグ // N : 動く対象の数字の位置 [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; M && ([A[a], A[N]] = [A[N], 15]); // 空きマスと数字の位置を交換 S = ""; // S : ブラウザに表示するテキスト 盤面その他を格納 for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); // 盤面を格納 if(g){ // ゲーム中ならば for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); // クリア判定を行い,f を更新 } } } // ブラウザに盤面を表示,クリアしたら "Win" の文字列も追加 D.innerHTML = S + (f ? "Win" : ""); } for(j = 999; j--;) F(4*Math.random() | 0); // 盤面初期化 g = 1; // 初期化終了,g を更新(フラグをゲーム中に設定) </script> 上から順番に見ていきましょう. <body id=D onKeyDown=F(event.which-37)> ブラウザ内でキーが押下されたとき(onKeyDown),押下されたキーのキーコード(event.which)から37を引いたものを取得して,関数Fに投げています. 矢印キーの←,↑,→,↓のキーコードがそれぞれ 37, 38, 39, 40 なので,37 を引くことで 0, 1, 2, 3 に変換し,扱いやすさ向上&コード短縮に役立てています. g = f = 0; gはゲームの最中であるか(盤面の初期化の最中ではないか)を示すフラグ,fはゲームをクリア済かを示すフラグです. どちらも0すなわちfalseで初期化しています. for(i = 16; i--; A.push(i)); 盤面配列Aに 0 から 15 までの整数を逆順で格納しています. このfor文の表記ですが,変数iの値を 16 で初期化した後に条件式内でiのデクリメントを行っています.これは,i=16から条件式がfalse,すなわちi=0になるまでiを減算し続ける,という実装であり, for(i = 15; i >= 0; i--) A.push(i); という実装と同様の動作をします. 次に,関数F(K)の内部を見ていきましょう. if(!f && 0 <= K && K < 4){ ... 先述の通り,fはゲームクリア済みか否かを示すフラグであり,0ならば未クリア,0以外ならばクリア済みであることを表します. 関数Fの引数であるKは先述した (キーコード-37) の値ですから,このif文は,ゲームが未クリア,かつ入力が矢印キーからならば(i.e. Kが 0, 1, 2, 3 のいずれかならば)実行されます. a = A.indexOf(15); [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; ここで出てくる変数の意味は以下の通り. a : 盤面中の空きマスのある位置(0 ~ 15 の整数) M : 矢印キー押下時に,指定された方向への数字の移動が可能かどうかのフラグ(0で移動不可能,0以外で移動可能) N : 移動可能な場合,移動する対象の数字がある場所(0 ~ 15 の整数) M, Nへの代入部は複雑なので,if文で書き換えてみます. if(K % 2){ if(--K) M = (a < 12); else M = (a > 3); N = a + ~-K * 4; }else{ M = (a + !!K) % 4; N = a + K - 1; } if文は,押下された矢印キーに応じて次のように動作します. 上下キー(K=1 or 3) : 1 -> true -> 前半ブロック実行 左右キー(K=0 or 2) : 0 -> false -> 後半ブロック実行 キー入力が上下キーの場合,さらに--Kの値,すなわち上下のどちらが入力されたかが評価され,Mの値は次のように設定されます. 上キー(K=3) : --K=2 -> true -> M = (a < 12); 空きマスの位置が最下行以外 (a < 12) のとき,数字をより上行へ移動可能 下キー(K=1) : --K=0 -> false -> M = (a > 3); 空きマスの位置最上行以外 (a > 3) のとき,数字をより下行へ移動可能 Nへの代入時における~-KはK-1と同値であり,演算子~, -の優先度が*よりも上であることから,コード短縮テクニックとして頻繁に使用されます(K+1と同値である-~Kも同様). また,Kの値は先程デクリメントした値からさらに -1 されることから,Nに代入される値は以下の通りになります. 上キー('K'=3) : ~-K=1 -> N = a+4 (空きマスの 1 行下にある数字が移動) 下キー(K=1) : ~-K=-1 -> N = a-4 (空きマスの 1 行上にある数字が移動) キー入力が左右の時は以下の通り. 右キー(K=2) : !!K=true -> !!K=1 -> M = (a+1)%4, N = a+1 空きマスが左端以外の列にあるとき,空きマスの 1 列左にある数字が移動 左キー(K=0) : !!K=false -> !!K=0 -> M = a%4, N = a-1 空きマスが右端以外の列にあるとき,空きマスの 1 列右にある数字が移動 ここでは,否定演算子!を 2 回重ねて使うことで,0ならば0,それ以外ならば1という変換を行っています. S = ""; for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); ここは盤面のテキスト化です.ここでも先述の短縮forループを利用しています. 盤面Aのk番目の要素について,それに対応する丸数字をとってきてSに加算しています. kが 4 で割り切れる,すなわち行末であるときに改行も加算します. if(g){ for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); } } ゲームの最中(g==true)であれば,if文内部でクリア判定を行い,フラグfを更新します. ここでは,いったんfを1(クリア済)で初期化した後,盤面配列Aの要素が昇順で並んでいれば,ループが終わったときにもfがtrueに保たれる(otherwise false),という実装にしています. ここでも先程の短縮forループを利用していますが,条件式のデクリメントが前置されているのは,rが 0 のときにループを抜けてほしいため(r-1に負になってほしくない)です. D.innerHTML = S + (f ? "Win" : ""); 盤面をテキスト化した文字列Sをブラウザに出力します. クリアしていれば(i.e. f==true)“Win”の文字列も一緒に出力します. これで関数F(K)の定義は終了です. for(j = 999; j--;) F(4*Math.random() | 0); g = 1; 最後に,ゲームの初期化処理を行っています. Fに 0, 1, 2, 3 のいずれかの数値を引数として代入して実行する処理を 999 回繰り返しています. すなわち,「とりあえずランダムな方向に数字を(最大)999 回動かす」という処理を行っています. 先程クリア判定のためにフラグgを用意しているのは,この盤面の初期化時にも関数Fを利用しているからであり,この時点でクリア判定が行われてしまうのを防ぐという目的があります. そして,初期化後にgをtrueに設定して,ゲーム中フラグを立てます. 最後に とりあえず 5 行に収めることには成功しましたが,もっとスマートな実装ができないものかという模索は今後も気が向いたときにやっていきたいなと思う所存であります. コードゴルフ楽しい!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

4行のHTMLで15パズル作ってみた(追記:5行から短縮)

概要 HTMLとJavaScriptを用い,「15パズルを実装する」というお題でコードゴルフ(ショートコーディング)をしてみました. 15パズルとは 15パズルは、スライディングブロックパズル(Sliding puzzle)のひとつである。4×4のボードの上に4×4-1すなわち15枚の駒があり、1駒ぶんの空きを利用して駒をスライドし、駒を目的の配置にする。(Wikipediaより) 要するに数字を動かして順番にそろえるコレです. レギュレーション 1行79文字 全角文字は1つにつき2文字とカウントする 基本的には,2ちゃんねるで行われていた「7行プログラミング」に準ずる形で設定しています. 遊び方 キーボードの矢印キーで,空きマスに隣接している数字を指定した方向に動かします. 数字の配置が のようになればゲームクリア. 実行環境 言語 : HTML, JavaScript 動作ブラウザ : Google Chrome, Microsoft Edge 他ブラウザでの動作確認はぶっちゃけめんどくさくてやっておりません.情報求む. 完成コード <body id=D onKeyDown=F(event.which-37) onLoad='A=[];g=f=0;for(i=16;i--;A.push(i)) ;s="①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ ";F=(K)=>{if(!f&&0<=K&&K<4){a=A.indexOf(15);[ M,N]=K%2?[--K?a<12:a>3,a+~-K*4]:[(a+!!K)%4,a+K-1];M&&([A[a],A[N]]=[A[N],15]);S="" ;for(k=16;k--;S+=s[A[k]]+(k%4?"":"<br>"));if(g)for(f=1,r=16;--r;)f&=(A[r-1]>A[r]) }D.innerHTML=S+(f?"Win":"")};for(j=999;j--;)F(4*Math.random()|0);g=1'> 418バイト.スクリプト本体は全てonLoad属性にぶっこんでいます. 解説 スクリプト本体をonLoad属性から出して,コードに適宜改行,スペースとコメントなどを挟んだものがこちらになります. <body id=D onKeyDown=F(event.which-37)><script> // A : 盤面配列 0~15の数値を格納 // 0~14 が丸数字 ①~⑮ に対応,15 は空きマス A = []; // g : ゲーム中か(初期化中でないか)否かのフラグ // f : ゲームクリア判定のフラグ g = f = 0; for(i = 16; i--; A.push(i)); // 盤面 A に 0~15 の数値を逆順で格納 s = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮ "; // s : 盤面表示用丸数字テキスト // F : キー入力を受け取り,盤面を更新し,ブラウザに描写する関数 F = (K) => { if(!f && 0 <= K && K < 4){ // 未クリア & 入力されたキーが矢印キーならば a = A.indexOf(15); // a : 空きマスの位置 // M : 入力した方向に数字が動かせるかのフラグ // N : 動く対象の数字の位置 [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; M && ([A[a], A[N]] = [A[N], 15]); // 空きマスと数字の位置を交換 S = ""; // S : ブラウザに表示するテキスト 盤面その他を格納 for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); // 盤面を格納 if(g){ // ゲーム中ならば for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); // クリア判定を行い,f を更新 } } } // ブラウザに盤面を表示,クリアしたら "Win" の文字列も追加 D.innerHTML = S + (f ? "Win" : ""); } for(j = 999; j--;) F(4*Math.random() | 0); // 盤面初期化 g = 1; // 初期化終了,g を更新(フラグをゲーム中に設定) </script> 上から順番に見ていきましょう. <body id=D onKeyDown=F(event.which-37)> ブラウザ内でキーが押下されたとき(onKeyDown),押下されたキーのキーコード(event.which)から37を引いたものを取得して,関数Fに投げています. 矢印キーの←,↑,→,↓のキーコードがそれぞれ 37, 38, 39, 40 なので,37 を引くことで 0, 1, 2, 3 に変換し,扱いやすさ向上&コード短縮に役立てています. g = f = 0; gはゲームの最中であるか(盤面の初期化の最中ではないか)を示すフラグ,fはゲームをクリア済かを示すフラグです. どちらも0すなわちfalseで初期化しています. for(i = 16; i--; A.push(i)); 盤面配列Aに 0 から 15 までの整数を逆順で格納しています. このfor文の表記ですが,変数iの値を 16 で初期化した後に条件式内でiのデクリメントを行っています.これは,i=16から条件式がfalse,すなわちi=0になるまでiを減算し続ける,という実装であり, for(i = 15; i >= 0; i--) A.push(i); という実装と同様の動作をします. 次に,関数F(K)の内部を見ていきましょう. if(!f && 0 <= K && K < 4){ ... 先述の通り,fはゲームクリア済みか否かを示すフラグであり,0ならば未クリア,0以外ならばクリア済みであることを表します. 関数Fの引数であるKは先述した (キーコード-37) の値ですから,このif文は,ゲームが未クリア,かつ入力が矢印キーからならば(i.e. Kが 0, 1, 2, 3 のいずれかならば)実行されます. a = A.indexOf(15); [M, N] = K%2 ? [--K ? a<12 : a>3, a+~-K*4] : [(a+!!K)%4, a+K-1]; ここで出てくる変数の意味は以下の通り. a : 盤面中の空きマスのある位置(0 ~ 15 の整数) M : 矢印キー押下時に,指定された方向への数字の移動が可能かどうかのフラグ(0で移動不可能,0以外で移動可能) N : 移動可能な場合,移動する対象の数字がある場所(0 ~ 15 の整数) M, Nへの代入部は複雑なので,if文で書き換えてみます. if(K % 2){ if(--K) M = (a < 12); else M = (a > 3); N = a + ~-K * 4; }else{ M = (a + !!K) % 4; N = a + K - 1; } if文は,押下された矢印キーに応じて次のように動作します. 上下キー(K=1 or 3) : 1 -> true -> 前半ブロック実行 左右キー(K=0 or 2) : 0 -> false -> 後半ブロック実行 キー入力が上下キーの場合,さらに--Kの値,すなわち上下のどちらが入力されたかが評価され,Mの値は次のように設定されます. 上キー(K=3) : --K=2 -> true -> M = (a < 12); 空きマスの位置が最下行以外 (a < 12) のとき,数字をより上行へ移動可能 下キー(K=1) : --K=0 -> false -> M = (a > 3); 空きマスの位置最上行以外 (a > 3) のとき,数字をより下行へ移動可能 Nへの代入時における~-KはK-1と同値であり,演算子~, -の優先度が*よりも上であることから,コード短縮テクニックとして頻繁に使用されます(K+1と同値である-~Kも同様). また,Kの値は先程デクリメントした値からさらに -1 されることから,Nに代入される値は以下の通りになります. 上キー('K'=3) : ~-K=1 -> N = a+4 (空きマスの 1 行下にある数字が移動) 下キー(K=1) : ~-K=-1 -> N = a-4 (空きマスの 1 行上にある数字が移動) キー入力が左右の時は以下の通り. 右キー(K=2) : !!K=true -> !!K=1 -> M = (a+1)%4, N = a+1 空きマスが左端以外の列にあるとき,空きマスの 1 列左にある数字が移動 左キー(K=0) : !!K=false -> !!K=0 -> M = a%4, N = a-1 空きマスが右端以外の列にあるとき,空きマスの 1 列右にある数字が移動 ここでは,否定演算子!を 2 回重ねて使うことで,0ならば0,それ以外ならば1という変換を行っています. S = ""; for(k = 16; k--; S += s[A[k]] + (k%4 ? "" : "<br>")); ここは盤面のテキスト化です.ここでも先述の短縮forループを利用しています. 盤面Aのk番目の要素について,それに対応する丸数字をとってきてSに加算しています. kが 4 で割り切れる,すなわち行末であるときに改行も加算します. if(g){ for(f = 1, r = 16; --r;){ f &= (A[r-1] > A[r]); } } ゲームの最中(g==true)であれば,if文内部でクリア判定を行い,フラグfを更新します. ここでは,いったんfを1(クリア済)で初期化した後,盤面配列Aの要素が昇順で並んでいれば,ループが終わったときにもfがtrueに保たれる(otherwise false),という実装にしています. ここでも先程の短縮forループを利用していますが,条件式のデクリメントが前置されているのは,rが 0 のときにループを抜けてほしいため(r-1に負になってほしくない)です. D.innerHTML = S + (f ? "Win" : ""); 盤面をテキスト化した文字列Sをブラウザに出力します. クリアしていれば(i.e. f==true)“Win”の文字列も一緒に出力します. これで関数F(K)の定義は終了です. for(j = 999; j--;) F(4*Math.random() | 0); g = 1; 最後に,ゲームの初期化処理を行っています. Fに 0, 1, 2, 3 のいずれかの数値を引数として代入して実行する処理を 999 回繰り返しています. すなわち,「とりあえずランダムな方向に数字を(最大)999 回動かす」という処理を行っています. 先程クリア判定のためにフラグgを用意しているのは,この盤面の初期化時にも関数Fを利用しているからであり,この時点でクリア判定が行われてしまうのを防ぐという目的があります. そして,初期化後にgをtrueに設定して,ゲーム中フラグを立てます. 最後に とりあえず 5 行に収めることには成功しましたが,もっとスマートな実装ができないものかという模索は今後も気が向いたときにやっていきたいなと思う所存であります. コードゴルフ楽しい! 追記 Qiita の皆様の叡智をお借りして,3行半以下まで短縮されました(コードと更新履歴はコメント欄を参照). 心より感謝申し上げます.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む