2020,江端さんの技術メモ

江端の環境でのローカルな場所 C:\Users\ebata\Desktop\bouncy2

C:\Users\ebata>cd C:\Users\ebata\Desktop\bouncy2
cd /c/Users/ebata/Desktop/bouncy2
python3 httpsrv.py
http://localhost:8080 で起動

 

  • 2020/03/14

     

  • 背景
    • ■これからは「C/C++ → Go」と「JavaScript → Webassembly」とシフトしながら色々やっていこうと思っている■正直、どっちも難しい。そういう時は、サンプルをパクって勉強するのが正解 ―― と思っている
  • 目的
  • 環境
    • ■Windows7 or 10
    • ■Go と python3 をインストールしてある
  • やってみたこと
    • ■サンプルプログラムをダウンロードして、コンパイルしてみた
      • ~ https://github.com/stdiopt/gowasm-experiments/tree/master/bouncy
      • コマンドプロンプトからこんな感じでコンパイルできた
        • $ set GOOS=js
        • $ set  GOARCH=wasm
        • $ go build -o main.wasm main.go
        • ちなみに、bashの環境があれば、build.shでコンパイルできる
      • 実際には、コンパイルしなくても、バイナリコード(main.wasm)も、ダウンロードの中に入っているので、コンパイルは不要だったが
    • ■ローカルサーバとしては、色々試してみた
      • python3 -m http.server 8080
      • Goでサーバも作ってみた
      • Perlでもやってみた
    • ■動画がどうしても出てこない
    • ■ここから丸2日間の格闘のスタート
      • もう、色々探しまくった
  • 確認していた問題点
    • ■Google Chromoから、→ 「その他のツール」 → 「ディベロッパーツール」 → 
      • Uncaught (in promise) TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'.この「Expected 'application/wasm'.」がどうにも、不味いらしい ―― が、解決方法が、どうにも見つからない
    • ■kobore.netのサーバに上げても、改善が見られず
  • ローカルサーバを作ってみた
    • ■httpsrv.pyを作った
      #!/usr/bin/env python3
      import http.server
        import socketserver
        
        PORT =
      8080
        
        Handler = http.server.SimpleHTTPRequestHandler
        Handler.extensions_map.update({
        '.wasm': 'application/wasm',
        })
        
        socketserver.TCPServer.allow_reuse_address =
      True
      with socketserver.TCPServer(("", PORT), Handler) as httpd:
        httpd.allow_reuse_address =
      True
      print("serving at port", PORT)
        httpd.serve_forever()
      
  • ■httpsrv.pyを起動した
    • $ python3 httpsrv.py
  • http://localhost:8080 で起動
    • ■動いた
      ■動かなかったら、chromo → 「設定」 → 「閲覧履歴データの削除」でキャッシュをクリアてみること

以上

httpsrv.py

#!/usr/bin/env python3

import http.server
import socketserver

PORT = 8080

Handler = http.server.SimpleHTTPRequestHandler
Handler.extensions_map.update({
    '.wasm': 'application/wasm',
})

socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    httpd.allow_reuse_address = True
    print("serving at port", PORT)
    httpd.serve_forever()

main.go

//Wasming
// compile: GOOS=js GOARCH=wasm go build -o main.wasm ./main.go
package main

import (
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"syscall/js"
)

var (
	width      float64
	height     float64
	mousePos   [2]float64
	ctx        js.Value    // "syscall/js"から引っ張られる
	lineDistSq float64 = 100 * 100
)

func main() {

	// Init Canvas stuff
	doc := js.Global().Get("document")
	canvasEl := doc.Call("getElementById", "mycanvas")
	width = doc.Get("body").Get("clientWidth").Float()
	height = doc.Get("body").Get("clientHeight").Float()
	canvasEl.Set("width", width)
	canvasEl.Set("height", height)
	ctx = canvasEl.Call("getContext", "2d")

	done := make(chan struct{}, 0)

	dt := DotThing{speed: 160, size: 6}

	mouseMoveEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		mousePos[0] = e.Get("clientX").Float()
		mousePos[1] = e.Get("clientY").Float()
		return nil
	})
	defer mouseMoveEvt.Release()

	// Event handler for count range
	countChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("Invalid value", err)
			return nil
		}
		dt.SetNDots(intVal)
		return nil
	})
	defer countChangeEvt.Release()

	// Event handler for speed range
	speedInputEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		fval, err := strconv.ParseFloat(evt.Get("target").Get("value").String(), 64)
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.speed = fval
		return nil
	})
	defer speedInputEvt.Release()

	// Event handler for size
	sizeChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.size = intVal
		return nil
	})
	defer sizeChangeEvt.Release()

	// Event handler for lines toggle
	lineChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.lines = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer lineChangeEvt.Release()

	// Event handler for dashed toggle
	dashedChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.dashed = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer dashedChangeEvt.Release()

	doc.Call("addEventListener", "mousemove", mouseMoveEvt)
	doc.Call("getElementById", "count").Call("addEventListener", "change", countChangeEvt)
	doc.Call("getElementById", "speed").Call("addEventListener", "input", speedInputEvt)
	doc.Call("getElementById", "size").Call("addEventListener", "input", sizeChangeEvt)
	doc.Call("getElementById", "dashed").Call("addEventListener", "change", dashedChangeEvt)
	doc.Call("getElementById", "lines").Call("addEventListener", "change", lineChangeEvt)

	dt.SetNDots(100)
	dt.lines = false
	var renderFrame js.Func
	var tmark float64
	var markCount = 0
	var tdiffSum float64

	renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		now := args[0].Float()
		tdiff := now - tmark
		tdiffSum += now - tmark
		markCount++
		if markCount > 10 {
			doc.Call("getElementById", "fps").Set("innerHTML", fmt.Sprintf("FPS: %.01f", 1000/(tdiffSum/float64(markCount))))
			tdiffSum, markCount = 0, 0
		}
		tmark = now

		// Pull window size to handle resize
		curBodyW := doc.Get("body").Get("clientWidth").Float()
		curBodyH := doc.Get("body").Get("clientHeight").Float()
		if curBodyW != width || curBodyH != height {
			width, height = curBodyW, curBodyH
			canvasEl.Set("width", width)
			canvasEl.Set("height", height)
		}
		dt.Update(tdiff / 1000)

		js.Global().Call("requestAnimationFrame", renderFrame)
		return nil
	})
	defer renderFrame.Release()

	// Start running
	js.Global().Call("requestAnimationFrame", renderFrame)

	<-done

}

// DotThing manager
type DotThing struct {
	dots   []*Dot
	dashed bool
	lines  bool
	speed  float64
	size   int
}

// Update updates the dot positions and draws
func (dt *DotThing) Update(dtTime float64) {
	if dt.dots == nil {
		return
	}
	ctx.Call("clearRect", 0, 0, width, height)

	// Update
	for i, dot := range dt.dots {
		dir := [2]float64{}
		// Bounce
		if dot.pos[0] < 0 {
			dot.pos[0] = 0
			dot.dir[0] *= -1
		}
		if dot.pos[0] > width {
			dot.pos[0] = width
			dot.dir[0] *= -1
		}

		if dot.pos[1] < 0 {
			dot.pos[1] = 0
			dot.dir[1] *= -1
		}

		if dot.pos[1] > height {
			dot.pos[1] = height
			dot.dir[1] *= -1
		}
		dir = dot.dir

		ctx.Set("globalAlpha", 0.5)
		ctx.Call("beginPath")
		ctx.Set("fillStyle", fmt.Sprintf("#%06x", dot.color))
		ctx.Set("strokeStyle", fmt.Sprintf("#%06x", dot.color))
		// Dashed array ref: https://github.com/golang/go/blob/release-branch.go1.11/src/syscall/js/js.go#L98
		ctx.Call("setLineDash", []interface{}{})
		if dt.dashed {
			ctx.Call("setLineDash", []interface{}{5, 10})
		}
		ctx.Set("lineWidth", dt.size)
		ctx.Call("arc", dot.pos[0], dot.pos[1], dt.size, 0, 2*math.Pi)
		ctx.Call("fill")

		mdx := mousePos[0] - dot.pos[0]
		mdy := mousePos[1] - dot.pos[1]
		d := math.Sqrt(mdx*mdx + mdy*mdy)
		if d < 200 {
			ctx.Set("globalAlpha", 1-d/200)
			ctx.Call("beginPath")
			ctx.Call("moveTo", dot.pos[0], dot.pos[1])
			ctx.Call("lineTo", mousePos[0], mousePos[1])
			ctx.Call("stroke")
			if d > 100 { // move towards mouse
				dir[0] = (mdx / d) * 2
				dir[1] = (mdy / d) * 2
			} else { // do not move
				dir[0] = 0
				dir[1] = 0
			}
		}

		if dt.lines {
			for _, dot2 := range dt.dots[i+1:] {
				mx := dot2.pos[0] - dot.pos[0]
				my := dot2.pos[1] - dot.pos[1]
				d := mx*mx + my*my
				if d < lineDistSq {
					ctx.Set("globalAlpha", 1-d/lineDistSq)
					ctx.Call("beginPath")
					ctx.Call("moveTo", dot.pos[0], dot.pos[1])
					ctx.Call("lineTo", dot2.pos[0], dot2.pos[1])
					ctx.Call("stroke")
				}
			}
		}

		dot.pos[0] += dir[0] * dt.speed * dtTime
		dot.pos[1] += dir[1] * dt.speed * dtTime
	}
}

// SetNDots reinitializes dots with n size
func (dt *DotThing) SetNDots(n int) {
	dt.dots = make([]*Dot, n)
	for i := 0; i < n; i++ {
		dt.dots[i] = &Dot{
			pos: [2]float64{
				rand.Float64() * width,
				rand.Float64() * height,
			},
			dir: [2]float64{
				rand.NormFloat64(),
				rand.NormFloat64(),
			},
			color: uint32(rand.Intn(0xFFFFFF)),
			size:  10,
		}
	}
}

// Dot represents a dot ...
type Dot struct {
	pos   [2]float64
	dir   [2]float64
	color uint32
	size  float64
}

build.sh

#!/bin/sh

GOOS=js GOARCH=wasm go build -o main.wasm ./main.go

wasm_exec.js

// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

(() => {
	// Map multiple JavaScript environments to a single common API,
	// preferring web standards over Node.js API.
	//
	// Environments considered:
	// - Browsers
	// - Node.js
	// - Electron
	// - Parcel

	if (typeof global !== "undefined") {
		// global already exists
	} else if (typeof window !== "undefined") {
		window.global = window;
	} else if (typeof self !== "undefined") {
		self.global = self;
	} else {
		throw new Error("cannot export Go (neither global, window nor self is defined)");
	}

	if (!global.require && typeof require !== "undefined") {
		global.require = require;
	}

	if (!global.fs && global.require) {
		global.fs = require("fs");
	}

	const enosys = () => {
		const err = new Error("not implemented");
		err.code = "ENOSYS";
		return err;
	};

	if (!global.fs) {
		let outputBuf = "";
		global.fs = {
			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
			writeSync(fd, buf) {
				outputBuf += decoder.decode(buf);
				const nl = outputBuf.lastIndexOf("\n");
				if (nl != -1) {
					console.log(outputBuf.substr(0, nl));
					outputBuf = outputBuf.substr(nl + 1);
				}
				return buf.length;
			},
			write(fd, buf, offset, length, position, callback) {
				if (offset !== 0 || length !== buf.length || position !== null) {
					callback(enosys());
					return;
				}
				const n = this.writeSync(fd, buf);
				callback(null, n);
			},
			chmod(path, mode, callback) { callback(enosys()); },
			chown(path, uid, gid, callback) { callback(enosys()); },
			close(fd, callback) { callback(enosys()); },
			fchmod(fd, mode, callback) { callback(enosys()); },
			fchown(fd, uid, gid, callback) { callback(enosys()); },
			fstat(fd, callback) { callback(enosys()); },
			fsync(fd, callback) { callback(null); },
			ftruncate(fd, length, callback) { callback(enosys()); },
			lchown(path, uid, gid, callback) { callback(enosys()); },
			link(path, link, callback) { callback(enosys()); },
			lstat(path, callback) { callback(enosys()); },
			mkdir(path, perm, callback) { callback(enosys()); },
			open(path, flags, mode, callback) { callback(enosys()); },
			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
			readdir(path, callback) { callback(enosys()); },
			readlink(path, callback) { callback(enosys()); },
			rename(from, to, callback) { callback(enosys()); },
			rmdir(path, callback) { callback(enosys()); },
			stat(path, callback) { callback(enosys()); },
			symlink(path, link, callback) { callback(enosys()); },
			truncate(path, length, callback) { callback(enosys()); },
			unlink(path, callback) { callback(enosys()); },
			utimes(path, atime, mtime, callback) { callback(enosys()); },
		};
	}

	if (!global.process) {
		global.process = {
			getuid() { return -1; },
			getgid() { return -1; },
			geteuid() { return -1; },
			getegid() { return -1; },
			getgroups() { throw enosys(); },
			pid: -1,
			ppid: -1,
			umask() { throw enosys(); },
			cwd() { throw enosys(); },
			chdir() { throw enosys(); },
		}
	}

	if (!global.crypto) {
		const nodeCrypto = require("crypto");
		global.crypto = {
			getRandomValues(b) {
				nodeCrypto.randomFillSync(b);
			},
		};
	}

	if (!global.performance) {
		global.performance = {
			now() {
				const [sec, nsec] = process.hrtime();
				return sec * 1000 + nsec / 1000000;
			},
		};
	}

	if (!global.TextEncoder) {
		global.TextEncoder = require("util").TextEncoder;
	}

	if (!global.TextDecoder) {
		global.TextDecoder = require("util").TextDecoder;
	}

	// End of polyfills for common API.

	const encoder = new TextEncoder("utf-8");
	const decoder = new TextDecoder("utf-8");

	global.Go = class {
		constructor() {
			this.argv = ["js"];
			this.env = {};
			this.exit = (code) => {
				if (code !== 0) {
					console.warn("exit code:", code);
				}
			};
			this._exitPromise = new Promise((resolve) => {
				this._resolveExitPromise = resolve;
			});
			this._pendingEvent = null;
			this._scheduledTimeouts = new Map();
			this._nextCallbackTimeoutID = 1;

			const setInt64 = (addr, v) => {
				this.mem.setUint32(addr + 0, v, true);
				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
			}

			const getInt64 = (addr) => {
				const low = this.mem.getUint32(addr + 0, true);
				const high = this.mem.getInt32(addr + 4, true);
				return low + high * 4294967296;
			}

			const loadValue = (addr) => {
				const f = this.mem.getFloat64(addr, true);
				if (f === 0) {
					return undefined;
				}
				if (!isNaN(f)) {
					return f;
				}

				const id = this.mem.getUint32(addr, true);
				return this._values[id];
			}

			const storeValue = (addr, v) => {
				const nanHead = 0x7FF80000;

				if (typeof v === "number") {
					if (isNaN(v)) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 0, true);
						return;
					}
					if (v === 0) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 1, true);
						return;
					}
					this.mem.setFloat64(addr, v, true);
					return;
				}

				switch (v) {
					case undefined:
						this.mem.setFloat64(addr, 0, true);
						return;
					case null:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 2, true);
						return;
					case true:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 3, true);
						return;
					case false:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 4, true);
						return;
				}

				let id = this._ids.get(v);
				if (id === undefined) {
					id = this._idPool.pop();
					if (id === undefined) {
						id = this._values.length;
					}
					this._values[id] = v;
					this._goRefCounts[id] = 0;
					this._ids.set(v, id);
				}
				this._goRefCounts[id]++;
				let typeFlag = 1;
				switch (typeof v) {
					case "string":
						typeFlag = 2;
						break;
					case "symbol":
						typeFlag = 3;
						break;
					case "function":
						typeFlag = 4;
						break;
				}
				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
				this.mem.setUint32(addr, id, true);
			}

			const loadSlice = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
			}

			const loadSliceOfValues = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				const a = new Array(len);
				for (let i = 0; i < len; i++) {
					a[i] = loadValue(array + i * 8);
				}
				return a;
			}

			const loadString = (addr) => {
				const saddr = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
			}

			const timeOrigin = Date.now() - performance.now();
			this.importObject = {
				go: {
					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
					// This changes the SP, thus we have to update the SP used by the imported function.

					// func wasmExit(code int32)
					"runtime.wasmExit": (sp) => {
						const code = this.mem.getInt32(sp + 8, true);
						this.exited = true;
						delete this._inst;
						delete this._values;
						delete this._goRefCounts;
						delete this._ids;
						delete this._idPool;
						this.exit(code);
					},

					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
					"runtime.wasmWrite": (sp) => {
						const fd = getInt64(sp + 8);
						const p = getInt64(sp + 16);
						const n = this.mem.getInt32(sp + 24, true);
						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
					},

					// func resetMemoryDataView()
					"runtime.resetMemoryDataView": (sp) => {
						this.mem = new DataView(this._inst.exports.mem.buffer);
					},

					// func nanotime1() int64
					"runtime.nanotime1": (sp) => {
						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
					},

					// func walltime1() (sec int64, nsec int32)
					"runtime.walltime1": (sp) => {
						const msec = (new Date).getTime();
						setInt64(sp + 8, msec / 1000);
						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
					},

					// func scheduleTimeoutEvent(delay int64) int32
					"runtime.scheduleTimeoutEvent": (sp) => {
						const id = this._nextCallbackTimeoutID;
						this._nextCallbackTimeoutID++;
						this._scheduledTimeouts.set(id, setTimeout(
							() => {
								this._resume();
								while (this._scheduledTimeouts.has(id)) {
									// for some reason Go failed to register the timeout event, log and try again
									// (temporary workaround for https://github.com/golang/go/issues/28975)
									console.warn("scheduleTimeoutEvent: missed timeout event");
									this._resume();
								}
							},
							getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
						));
						this.mem.setInt32(sp + 16, id, true);
					},

					// func clearTimeoutEvent(id int32)
					"runtime.clearTimeoutEvent": (sp) => {
						const id = this.mem.getInt32(sp + 8, true);
						clearTimeout(this._scheduledTimeouts.get(id));
						this._scheduledTimeouts.delete(id);
					},

					// func getRandomData(r []byte)
					"runtime.getRandomData": (sp) => {
						crypto.getRandomValues(loadSlice(sp + 8));
					},

					// func finalizeRef(v ref)
					"syscall/js.finalizeRef": (sp) => {
						const id = this.mem.getUint32(sp + 8, true);
						this._goRefCounts[id]--;
						if (this._goRefCounts[id] === 0) {
							const v = this._values[id];
							this._values[id] = null;
							this._ids.delete(v);
							this._idPool.push(id);
						}
					},

					// func stringVal(value string) ref
					"syscall/js.stringVal": (sp) => {
						storeValue(sp + 24, loadString(sp + 8));
					},

					// func valueGet(v ref, p string) ref
					"syscall/js.valueGet": (sp) => {
						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
						sp = this._inst.exports.getsp(); // see comment above
						storeValue(sp + 32, result);
					},

					// func valueSet(v ref, p string, x ref)
					"syscall/js.valueSet": (sp) => {
						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
					},

					// func valueDelete(v ref, p string)
					"syscall/js.valueDelete": (sp) => {
						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
					},

					// func valueIndex(v ref, i int) ref
					"syscall/js.valueIndex": (sp) => {
						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
					},

					// valueSetIndex(v ref, i int, x ref)
					"syscall/js.valueSetIndex": (sp) => {
						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
					},

					// func valueCall(v ref, m string, args []ref) (ref, bool)
					"syscall/js.valueCall": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const m = Reflect.get(v, loadString(sp + 16));
							const args = loadSliceOfValues(sp + 32);
							const result = Reflect.apply(m, v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 56, result);
							this.mem.setUint8(sp + 64, 1);
						} catch (err) {
							storeValue(sp + 56, err);
							this.mem.setUint8(sp + 64, 0);
						}
					},

					// func valueInvoke(v ref, args []ref) (ref, bool)
					"syscall/js.valueInvoke": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.apply(v, undefined, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueNew(v ref, args []ref) (ref, bool)
					"syscall/js.valueNew": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.construct(v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueLength(v ref) int
					"syscall/js.valueLength": (sp) => {
						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
					},

					// valuePrepareString(v ref) (ref, int)
					"syscall/js.valuePrepareString": (sp) => {
						const str = encoder.encode(String(loadValue(sp + 8)));
						storeValue(sp + 16, str);
						setInt64(sp + 24, str.length);
					},

					// valueLoadString(v ref, b []byte)
					"syscall/js.valueLoadString": (sp) => {
						const str = loadValue(sp + 8);
						loadSlice(sp + 16).set(str);
					},

					// func valueInstanceOf(v ref, t ref) bool
					"syscall/js.valueInstanceOf": (sp) => {
						this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
					},

					// func copyBytesToGo(dst []byte, src ref) (int, bool)
					"syscall/js.copyBytesToGo": (sp) => {
						const dst = loadSlice(sp + 8);
						const src = loadValue(sp + 32);
						if (!(src instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					// func copyBytesToJS(dst ref, src []byte) (int, bool)
					"syscall/js.copyBytesToJS": (sp) => {
						const dst = loadValue(sp + 8);
						const src = loadSlice(sp + 16);
						if (!(dst instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					"debug": (value) => {
						console.log(value);
					},
				}
			};
		}

		async run(instance) {
			this._inst = instance;
			this.mem = new DataView(this._inst.exports.mem.buffer);
			this._values = [ // JS values that Go currently has references to, indexed by reference id
				NaN,
				0,
				null,
				true,
				false,
				global,
				this,
			];
			this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
			this._ids = new Map();  // mapping from JS values to reference ids
			this._idPool = [];      // unused ids that have been garbage collected
			this.exited = false;    // whether the Go program has exited

			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
			let offset = 4096;

			const strPtr = (str) => {
				const ptr = offset;
				const bytes = encoder.encode(str + "\0");
				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
				offset += bytes.length;
				if (offset % 8 !== 0) {
					offset += 8 - (offset % 8);
				}
				return ptr;
			};

			const argc = this.argv.length;

			const argvPtrs = [];
			this.argv.forEach((arg) => {
				argvPtrs.push(strPtr(arg));
			});
			argvPtrs.push(0);

			const keys = Object.keys(this.env).sort();
			keys.forEach((key) => {
				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
			});
			argvPtrs.push(0);

			const argv = offset;
			argvPtrs.forEach((ptr) => {
				this.mem.setUint32(offset, ptr, true);
				this.mem.setUint32(offset + 4, 0, true);
				offset += 8;
			});

			this._inst.exports.run(argc, argv);
			if (this.exited) {
				this._resolveExitPromise();
			}
			await this._exitPromise;
		}

		_resume() {
			if (this.exited) {
				throw new Error("Go program has already exited");
			}
			this._inst.exports.resume();
			if (this.exited) {
				this._resolveExitPromise();
			}
		}

		_makeFuncWrapper(id) {
			const go = this;
			return function () {
				const event = { id: id, this: this, args: arguments };
				go._pendingEvent = event;
				go._resume();
				return event.result;
			};
		}
	}

	if (
		global.require &&
		global.require.main === module &&
		global.process &&
		global.process.versions &&
		!global.process.versions.electron
	) {
		if (process.argv.length < 3) {
			console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
			process.exit(1);
		}

		const go = new Go();
		go.argv = process.argv.slice(2);
		go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
		go.exit = process.exit;
		WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
			process.on("exit", (code) => { // Node.js exits if no event handler is pending
				if (code === 0 && !go.exited) {
					// deadlock, make Go print error and stack traces
					go._pendingEvent = { id: 0 };
					go._resume();
				}
			});
			return go.run(result.instance);
		}).catch((err) => {
			console.error(err);
			process.exit(1);
		});
	}
})();

江端さんの技術メモ

11月23日 17:00ー

  • 1.背景
    • 11月23日17:00より通信系の改修を行うが、これまで一部の成功に目を取られ、全体を通じた実験を行わなかった為に、出戻りが大量に発生した、と考えている
    • このような再発を防止する為、全体を網羅するチェックリストを作成し、チェック漏れを防ぎ、全体の進行イメージを共有す
  • 2.目的
    • 「動くハズ」は全て棄却。4台の全部で目的の機能を発揮していることを確認する
      • (1)位置情報と乗客数の情報が、AGISに上っていることを確認する
      • (2)DBに乗降情報が正しく反映されていることを確認する
      • (3)自動車等を動かして、上記(1)(2)が正しく実施されていることを確認する
  • 3.事前準備
    • (0)装備
      • 完全冬装備+雨装備
      • 食料、飲料水は各自で調達
      • パソコン等の機材
      • SDカードリードライタ
      • スマホ
    • (1)配置
      • 開発チーム
        • 開発チームは事務所の中から、自動車およびカートの無線LANにアクセスして、確認およびデバッグを行う
      • 操作チーム
        • 操作チームは、自動車の中で、開発チームの支持を受けて、カートの操作を行う
      • 検証チーム
        • 検証チームは、AGISの内容をチェックし続ける
    • (2)チーム間連絡
      • 開発チームと操作チームは、携帯電話で連絡を取り合って、操作タイミングを合わせる
    • (3)その他
      • 休憩は各自が独自の判断で行う
  • 4.チェック項目
    • (0)準備段階
      • DSの試運転を実施したか(江端)
        • 実施した
      • CTの運転は実施したか(江端)
        • 夜間運転は危険なので取り下げ
      • SDカードのバックアップは取ったか
        • 3台完了
        • YHの20日改造バージョンだけが間に合わなかった
        • 22日の運用後に作成する
    • (1)実験前のチェック
      • ■「DBに乗降状態が全く反映されないメールが送付されてくる」という問題がある
        • 本日と昨日のDBの記載内容を確認する
        • DBの記載が性格であればメールのプログラムを確認する
        • この問題が解決していない場合、自動車等を使った実験は実施しない
      • ■ラズパイがGPS情報を取得できなかった場合、その理由を記載するDBまたはログの記載は正しく表示されているかを確認する
        • この問題が解決しない場合、自動車等を使った実験は実施しない
    • (2)1台目実験(D1号機)
      • (a)自動車静止状態
        • ■ラズパイのGPS取得は確認したか
          • この状態でもみちびきGPSの値はゆらぐので、GPS情報が全く同じになることはない
          • GPS情報はA-GISに反映されているか
        • ■乗降状態は正しくDBに反映されているか
          • 乗降状態は、実際に車の中のタブレットを使って実施する
          • 全部のDBの内容をチェック
        • ■GPSと乗降状態をA-GISは反映しているか
      • (b)自動車移動状態
        • ■ラズパイのGPS取得は確認したか(同上)
        • ■乗降状態は正しくDBに反映されているか(同上)
        • ■GPSと乗降状態をA-GISは反映しているか(同上)
    • (3)2台目実験(D2号機)
      • (a)自動車静止状態(同上)
      • (b)自動車移動状態(同上)
    • (4)3台目実験((Y1号機)
      • (a)自動車静止状態(同上)
        • 移動実験は行わず、GPS情報のゆらぎで代替とする
    • (5)4台目実験((Y2号機)
      • 同上
  • 以上

2019,江端さんの技術メモ

/*
  g++ -g specific_skill2.cpp -o specific_skill2
*/

/*

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> 



typedef struct person{
  int age;
  int type1_lifetime;  // 1号の残り時間
  int retirement_age; // 65歳で引退

  ///////////
  struct person *prev;  /* 前の構造体を示すポインタ */
  struct person *next;  /* 次の構造体を示すポインタ */ 

} PERSON;


int int_max(int a, int b)
{
  if (a < b) 
	return b;
  else 
	return a;
}



PERSON *p_first_person, *p_last_person;  

void init_person_list(PERSON **p_first_person, PERSON **p_last_person)
{
  PERSON *p_top_person = (PERSON *)malloc(sizeof(PERSON));
  if(p_top_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(p_top_person, 0, sizeof(PERSON)); // ゼロクリア


  PERSON *p_tail_person = (PERSON *)malloc(sizeof(PERSON));
  if(p_tail_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(p_tail_person, 0, sizeof(PERSON)); // ゼロクリア

  *p_first_person = p_top_person;
  *p_last_person = p_tail_person;

  (*p_first_person)->prev = NULL;
  (*p_last_person)->next = NULL;
  (*p_first_person)->next = (*p_last_person);
  (*p_last_person)->prev = (*p_first_person);

  return;
}

void add_person(PERSON *p_ref_person)
{

  PERSON *new_p_person = (PERSON *)malloc(sizeof(PERSON));
  if(new_p_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(new_p_person, 0, sizeof(PERSON)); // ゼロクリア
  memcpy(new_p_person, p_ref_person, sizeof(PERSON)); // 引数の動的メモリの内容コピー
 
  // personの追加属性記述ここから
 
  // personの追加属性記述ここまで

  PERSON *p_person = p_last_person->prev;

  p_person->next = new_p_person;
  new_p_person->prev = p_person;

  p_last_person->prev = new_p_person;
  new_p_person->next = p_last_person;

  return;
}

void delete_person(PERSON *p_person)  
{
  // ポインタを貼り替えて
  p_person->prev->next = p_person->next;
  p_person->next->prev = p_person->prev;
  
  // そのメモリを解放する
  free(p_person);

  return;

}

int type1(int k){
  int number = 40000 + 4000 * (k - 2019);
  if (number > 60000){
	number = 60000;
  }
  return number;
}

// 2.1.1 C言語のrand関数を用いた方法
double Uniform( void ){
    return ((double)rand()+1.0)/((double)RAND_MAX+2.0);
}

// 3.3. 正規分布・ガウス分布 (Normal Distribution)
double rand_normal( double mu, double sigma ){
  double z=sqrt( -2.0*log(Uniform()) ) * sin( 2.0*M_PI*Uniform() );
  return mu + sigma*z;
} 


int main()
{
  srand(13);

  // 社員格納用リストの作成
  init_person_list(&p_first_person, &p_last_person); 

  int pass1_number;

  for (int i = 2019; i < 2080; i++){
  // for (int i = 2019; i < 2200; i++){
	// 1号試験パス者の生成
	pass1_number = type1(i);

	for (int k = 0; k < pass1_number; k++){
	  PERSON person;
	  person.age = int_max((int)rand_normal(26.0, 3.5), 18); // 18歳以下は応募できない
	  // printf("person.age = %d\n",person.age);
	  person.type1_lifetime = 5;
	  person.retirement_age = 65;
	  
	  add_person(&person);
	} // for (int k = 0, k < type1(k); k++)
	// 1号試験パス者の生成(ここまで)

    // ===== PERSON ループを回す========
    PERSON* p_person = p_first_person->next;  
    while (p_person != p_last_person){
	  
	  // 1号 強制帰国
	  if (p_person->type1_lifetime == 0){  // 5年を使い果した
		if (Uniform() <= 0.4){   // 2号合格率 40%
		  p_person->type1_lifetime = -1;  // 免責フラグ
		}
		else{
		  PERSON* p_person_prev = p_person->prev;
		  delete_person(p_person);
		  p_person = p_person_prev;
		}
	  }
	  
	  if (p_person->age > 65){  // 65歳以上になったら、強制帰国
		PERSON* p_person_prev = p_person->prev;
		delete_person(p_person);
		p_person = p_person_prev;
	  }

	  // 次のループに回る
	  p_person = p_person->next;
	}

    p_person = p_first_person->next;  
    while (p_person != p_last_person){

	  // 1号 強制帰国を迎えるまでのタイムリミットを減らす
	  if (p_person->type1_lifetime != -1){
		p_person->type1_lifetime -= 1;
	  }

	  // 全員の年齢の加算
	  p_person->age += 1;

	  // 次のループに回る
	  p_person = p_person->next;
	}


	int type1_person_count = 0;
	int type2_person_count = 0;
    p_person = p_first_person->next;  
    while (p_person != p_last_person){

	  if (p_person->type1_lifetime != -1)
		type1_person_count += 1;
	  else
		type2_person_count += 1;		

	  p_person = p_person->next;
	}

	//printf("i:%d, type1:%d, type2:%d\n", i, type1_person_count, type2_person_count);
	printf("%d, %d, %d, %d\n", 
		   i, 
		   type1_person_count, 
		   type2_person_count,
		   type1_person_count + type2_person_count);


  }//   for (int i = 2019; i < 2100; i++){

  return 0;
}

2018,江端さんの技術メモ

    • 2018-08-26(日) 09:57:44
  • 1.問題
    • 文字通り、この問題で嵌った
    • 具体的には、ping google.comをしても "unknown"と言われる
    • 本当に困ったのは、名前解決する為のsudo apt-get update, sudo apt-get installが全く使えなくなったこと
  • 2.解決法
    • 結果として問題は解決した
    • いろいろ弄ってみたが、どれが原因で動き出したのかよく分からない
    • 結果的に、やったことを「逆順」に説明する
  • 3.前提
    • raspberry pi をホームサーバ(監視システム)として運用しているので、固定IPアドレス(192.168.0.10)で使っている。
    • DHCPサーバに、このラズパイのMACアドレスを登録しての適用除外としている
  • 4.結果的に、やったことを「逆順」に説明する
    • Step N
      • (1)IPV6のサポートを外す
        • /etc/sysctl.conf」に以下の2行を追加。
        • net.ipv6.conf.all.disable_ipv6 = 1
        • net.ipv6.conf.default.disable_ipv6 = 1
      • (2)DNSのネームサーバ設定
        • /etc/resolvconf/resolv.conf.d/baseに以下の1行を追加
        • nameserver 8.8.8.8
      • (3)どちらも設定したらUbuntuを再起動
    • Step N-1
      • /etc/nsswitch.confを編集
        • # hosts:          files mdns4_minimal [NOTFOUND=return] dns
        • hosts:          files dns
    • Step N-2
      • /etc/network/interfacesを編集 (dns-servers とか dns-nameservers とかを)
        • # interfaces(5) file used by ifup(8) and ifdown(8)
        • # Include files from /etc/network/interfaces.d:
        • source-directory /etc/network/interfaces.d
        • # The loopback network interface
        • auto lo
        • iface lo inet loopback
        • auto eth0
          • iface eth0 inet static
          • address 192.168.0.10
          • netmask 255.255.255.0
          • gateway 192.168.0.1
          • dns-namaservers 8.8.8.8
        • # dns-servers 8.8.8.8
        • # dns-nameservers 8.8.8.8
    • Step N-3
      • /etc/NetworkManager/NetworkManager.conf の編集は、しなかった

        • しかし、この内容も、なんとも怪しさ満載である
  • 5.考察
    • 諦めよう
      • アップグレードで、勝手に仕様変更されて動かなくなる、というケースがママある。今回もそれに嵌った可能性が高い
      • とても迷惑であるが、OSSと一緒に生きていくなら、「仕方ない」のかもしれない(仕様を変更して、改善を図ろうとする開発者を責められない)
      • 人生の時間を有意義に使いたのであれば、「OSSとつきあわない」が正解なのだろう、と思う。

2017,江端さんの技術メモ

/* 
   gcc -g ql_test.cpp -o ql_test
 
   強化学習(Q-Learning)を理解する為に、中学→高校→大学の学歴を使ってみた
 
*/
 
#include <stdio.h>
#include <stdlib.h>
 
typedef enum period{
  BIRTH = 0, JUNIOR_HIGH = 1, HIGH = 2, COLLEGE = 3, SUPER_COLLEGE = 4
}PERIOD;
 
typedef struct state{
  struct state* future_state[2]; // 未来へのパス(取り敢えず2つほど)
  PERIOD period;
  int q;
}STATE;
 
 
STATE* change_state(STATE* p_state)
{
  if ((double)rand()/RAND_MAX < 0.3){  // ε:0.3
	if ((double)rand()/RAND_MAX < 0.5){ // 半々
	  return p_state->future_state[0];
	}
	else{
	  return p_state->future_state[1];
	}
  }
  else {
	if (p_state->future_state[0]->q > p_state->future_state[1]->q){
	  return p_state->future_state[0];
	}
	else{
	  return p_state->future_state[1];
	}
  }
}
	
void  q_renewal(STATE* p_state)
{
  int dummy_q;
 
  if (p_state->period  == SUPER_COLLEGE){
	p_state->q += 0.1 * (1000- p_state->q); // α:0.1 報酬の源泉:年収1000万円
  }
  else if (p_state->period !=  COLLEGE){
	if (p_state->future_state[0]->q > p_state->future_state[1]->q){
	  dummy_q = p_state->future_state[0]->q;
	}
	else {
	  dummy_q = p_state->future_state[1]->q;
	}
	p_state->q += 0.1 * (0.9 * dummy_q - p_state->q); // α:0.1 γ:0.9
  }
 
  return;
}
	
void q_display(STATE* p_state)
{
  for (int i =0; i < 15 ; i++){
	printf("%d,", p_state->q);
	p_state++;
  }
  printf("\n");
  return;
}
 
 
 
int main()
{
  srand(13);
 
 
  // 初期設定
  //STATE* state;
  STATE state[15];
 
  state[0].period = BIRTH;
  state[0].future_state[0] = &(state[1]);
  state[0].future_state[1] = &(state[2]);
 
  state[1].period = JUNIOR_HIGH;
  state[1].future_state[0] = &(state[3]);
  state[1].future_state[1] = &(state[4]);
 
  state[2].period = JUNIOR_HIGH;
  state[2].future_state[0] = &(state[5]);
  state[2].future_state[1] = &(state[6]);
 
  state[3].period = HIGH;
  state[3].future_state[0] = &(state[7]);
  state[3].future_state[1] = &(state[8]);
 
  state[4].period = HIGH;
  state[4].future_state[0] = &(state[9]);
  state[4].future_state[1] = &(state[10]);
 
  state[5].period = HIGH;
  state[5].future_state[0] = &(state[11]);
  state[5].future_state[1] = &(state[12]);
 
  state[6].period = HIGH;
  state[6].future_state[0] = &(state[13]);
  state[6].future_state[1] = &(state[14]);
 
  state[7].period = COLLEGE;
  state[8].period = COLLEGE;
  state[9].period = COLLEGE;
  state[10].period = SUPER_COLLEGE;
  state[11].period = COLLEGE;
  state[12].period = COLLEGE;
  state[13].period = COLLEGE;
  state[14].period = COLLEGE;
  
  for (int i = 0; i < 15; i++){
	state[i].q = (int)rand() % 100;
  }
 
  printf("誕生,A中学,B中学,C高校,D高校,E高校,F高校,G大学,H大学,I大学,J大学,K大学,L大学,M大学,N大学\n");

  STATE* s = state;
  //q_display(s);
  q_display(state);
 
  for (int i = 0; i < 1000; i++){  // 300:学習回数
	STATE* s = state; // 初期値に戻しているだけ
	
	do{ 
	  s = change_state(s);
	  q_renewal(s);
	}while( (s->period != COLLEGE) && (s->period != SUPER_COLLEGE));

	q_display(state);

  }
 
  printf("\n[after]\n");
  //q_display(s);
  q_display(state);
 
}

2017,江端さんの技術メモ

/*
  gcc -g second_job.cpp -o second_job
 

  考え方
  
  (1)8時間労働、8時間睡眠、8時間余暇を基本として考える。
  (2)8時間余暇の中には、通勤時間1.5時間 食事時間1.5時間が含まれるものとする
  (3)とすれば、残りの余暇5時間をどのような使い方をするのかが問題となる。
  
  (4)十分な余暇は、基本的に正業のパフォーマンスを上げるものであるとする。
  (4)余暇を使った副業は、収入になるものとする

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct person {
  double first_business_hour;
  double sleep_hour;
  double commute_time;
  double meal_time;

  double max_remain_time;
  double second_business_hour;
  double final_remain_time;

  double fb_fee;
  double sb_fee;
  
  double fatigue_func;
  double cost;

  struct person *prev;  /* 前の構造体を示すポインタ */
  struct person *next;  /* 次の構造体を示すポインタ */
} PERSON;


double min(double a, double b)
{
  if (a > b)
	return b;
  else
	return a;
}

double diff(double a, double b)
{
  if (a > b)
	return a - b;
  else
	return 0;
}


double fatigue_func(double time)
{
  if (time < 1.0){
	return 0.5;
  }
  else if ((time >= 1.0) && (time < 3.0)){
	return (1.0 - 0.5)/(3.0 - 1.0) * (time - 1.0) + 0.5;
  }
  else {
	return 1.0;
  }
}

const double First_Business_hourly_fees = 2000;
const double First_Business_extra_fees = First_Business_hourly_fees * 1.25;
const double Second_Business_hourly_fees = 1000;


int main()
{
  srand(13);

  PERSON* first_p_person= (PERSON*)malloc(sizeof(PERSON));
  PERSON* last_p_person= (PERSON*)malloc(sizeof(PERSON));

  PERSON*  p_prev_person = first_p_person;

  for (int i = 0; i < 100; i++){
	
	PERSON* p_person= (PERSON*)malloc(sizeof(PERSON));
	memset(p_person, 0, sizeof(PERSON));


	//////// ポインタの貼り替え //////////
	p_prev_person->next = p_person;
	p_person->next = last_p_person;
	p_person->prev = p_prev_person;
	p_prev_person = p_person;
	//////////////////////////////////////

	p_person->first_business_hour = 8.0 + 2.0 * (double)rand()/RAND_MAX;    // 8~10時間

	p_person->first_business_hour = 8.0;    // 8~10時間

	p_person->sleep_hour = 7.0 + 1.0 * (1.0 - 2.0 * (double)rand()/RAND_MAX); // 6~8時間
	p_person->commute_time = 1.0 + 0.5 * (1.0 - 2.0 * (double)rand()/RAND_MAX); // 0.5~1.5時間
	p_person->meal_time = 1.0 + 0.5 * (1.0 - 2.0 * (double)rand()/RAND_MAX);  // 0.5~1.5時間
	
	p_person->max_remain_time = 
	  24.0 - 
	  p_person->first_business_hour -
	  p_person->sleep_hour - 
	  p_person->commute_time - 
	  p_person->meal_time;    // 最悪でも3時間の、最良で9時間の余暇時間ができる

#if 1
	p_person->second_business_hour = p_person->max_remain_time * (double)rand()/RAND_MAX; //余暇の時間を適当に振る
#else
	p_person->second_business_hour = 0;
#endif

	p_person->final_remain_time = p_person->max_remain_time - p_person->second_business_hour;
	
	p_person->fb_fee = 
	  min(p_person->first_business_hour, 8.0) * First_Business_hourly_fees +
	  diff(p_person->first_business_hour, 8.0) * First_Business_extra_fees;
	
	p_person->sb_fee = p_person->second_business_hour * Second_Business_hourly_fees;
	p_person->fatigue_func = fatigue_func(p_person->final_remain_time);

	p_person->cost =	
	  p_person->fb_fee * p_person->fatigue_func + p_person->sb_fee;
  
	//printf("%d:cost = %f\n", i, p_person->cost);

  }


  double total_cost = 0.0;
  
  PERSON* p_person = first_p_person->next;

  printf("本業時間,睡眠時間,通勤時間,食事時間,余暇時間,副業時間,残余暇時間,RATIO,収入\n");

  while(p_person->next != last_p_person){
	total_cost += p_person->cost;
	
	printf("%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
		   p_person->first_business_hour,
		   p_person->sleep_hour,
		   p_person->commute_time,
		   p_person->meal_time,
		   p_person->max_remain_time,
		   p_person->second_business_hour,
		   p_person->final_remain_time,
		   p_person->fatigue_func,
		   p_person->cost
		   );
	
	p_person = p_person->next;

  }
  
  printf("total cost = %f\n", total_cost);  


}

2017,江端さんの技術メモ

/*
  gcc -g hiseiki3.cpp -o hiseiki3
*/

/*
  まず「基本形」を崩す

  非正規社員投入の方針は、
  (1)潜在的な市場が増加中なら投入
  (2)赤字に転じたら、直ちに非正規社員を全員解雇
  という極めて単純なもの
  
  に、

  非正規社員を保護する
  最低3年間は解雇できないもの、としてみる


*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> 



typedef struct person{
  int age;  // 20歳~60歳 のいずれか
  int position;  // 1:正規社員 0:非正規社員
  double productivity;  // 生産力 正規社員の場合 

  ///////////
  struct person *prev;  /* 前の構造体を示すポインタ */
  struct person *next;  /* 次の構造体を示すポインタ */ 

} PERSON;


typedef struct company{
  int payroll;
  double productivity;  // 会社としての生産力の合計   
} COMPANY;


PERSON *p_first_person, *p_last_person;   //社員リスト(あえてグローバルで保持)

// 社員用リストの先頭と終端を作成するルーチン
/*
  リストの先頭と終端をはグローバルでも良いのであるが、
  一応、メインルーチンの方で、陽に定義できるように、
  ここでは、返り値としている
*/

double min(double a, double b){
    if (b > a) 
        return a;
    else 
        return b;
};

double juglar_cycles(int year)
{
  // 10年で変動するsin周期
  // ベースとするのは2017年とする(単なる仮説)
  
  int x = (year -7) % 10;
  double y = sin( (double)x /10.0 * 2 * 3.141592654);

  return y;
}
  

void init_person_list(PERSON **p_first_person, PERSON **p_last_person)
{
  PERSON *p_top_person = (PERSON *)malloc(sizeof(PERSON));
  if(p_top_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(p_top_person, 0, sizeof(PERSON)); // ゼロクリア


  PERSON *p_tail_person = (PERSON *)malloc(sizeof(PERSON));
  if(p_tail_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(p_tail_person, 0, sizeof(PERSON)); // ゼロクリア

  *p_first_person = p_top_person;
  *p_last_person = p_tail_person;

  (*p_first_person)->prev = NULL;
  (*p_last_person)->next = NULL;
  (*p_first_person)->next = (*p_last_person);
  (*p_last_person)->prev = (*p_first_person);

  return;
}

// 社員オブジェクトを生成して、社員用リストに追加するルーチン
void add_person(PERSON *p_ref_person)
{

  PERSON *new_p_person = (PERSON *)malloc(sizeof(PERSON));
  if(new_p_person == NULL) {
	printf("メモリが確保できません\n");
	exit(EXIT_FAILURE);
  }
  memset(new_p_person, 0, sizeof(PERSON)); // ゼロクリア
  memcpy(new_p_person, p_ref_person, sizeof(PERSON)); // 引数の動的メモリの内容コピー
 
  // personの追加属性記述ここから
 


  // personの追加属性記述ここまで

  PERSON *p_person = p_last_person->prev;

  p_person->next = new_p_person;
  new_p_person->prev = p_person;

  p_last_person->prev = new_p_person;
  new_p_person->next = p_last_person;

  return;
}

void delete_person(PERSON *p_person)  
{
  // ポインタを貼り替えて
  p_person->prev->next = p_person->next;
  p_person->next->prev = p_person->prev;
  
  // そのメモリを解放する
  free(p_person);

  return;

}


int main()
{

  double potential = juglar_cycles(2017) * 1500.0 + 7500.0;
  double border = 7000.0; // 利益のボーダー(固定と考える)

  COMPANY company;
  company.productivity = 7437.500000; // 生産力初期値
  
   // 社員格納用リストの作成
  init_person_list(&p_first_person, &p_last_person); 
  
  //20歳から59歳までの正規社員、各世代100人づつ、合計4000人を作成する
  
  /////// PERSONを作成する ///////
  for (int age = 20; age < 60; age ++){
	for (int i = 0; i < 100; i++){
	  
	  PERSON person;
	  
	  person.age = age;
	  person.position = 1;  // 1:正規社員 0:非正規社員
	  person.productivity = 1.25 + (2.50 -1.25) / 40.0 * ((double)age -20.0); //20歳 1.25 60歳 2.50まで線形変化
	  
	  add_person(&person);
	}
  }
  /////// PERSONを作成する ///////  
 
  double sum_profit = 0;
  
  for ( int year = 2017; year < 2040; year++){
	
	//// 年齢を1歳加算し、定年
	// ===== PERSON ループを回す========
	PERSON* p_person = p_first_person->next;  
	while (p_person != p_last_person){
	  
	  p_person->age += 1;

	  if (p_person->position == 1){ // 正規社員であれば、生産力は年齢とともに高くなる(という仮説)
		p_person->productivity = 1.25 + (2.50 -1.25) / 40.0 * ((double)(p_person->age) -20.0); //20歳 1.25 60歳 2.50まで線形変化
	  }
	  else if (p_person->position == 0){ // 非正規社員であれば、生産力は年齢と関係なく高くならない(という仮説)
		p_person->productivity = 1.0;
	  }
	  
	  if (p_person->age >= 60){ // 60歳になったら定年(正規社員だろうが、非正規社員だろうが) 
		delete_person(p_person);
	  }
	  p_person = p_person->next;
	}
	// ===== PERSON ループを回す(ここまで)========
	
	//// 100人の20歳の新入社員を入社する
	for (int i = 0; i < 100; i++){
	  /////// PERSONを作成する ///////
	  PERSON person;
	  
	  person.age = 20;
	  person.position = 1;  // 1:正規社員 0:非正規社員
	  person.productivity = 1.25 + (2.50 -1.25) / 40.0 * ((double)person.age -20.0); //20歳 1.25 60歳 2.50まで線形変化
	  
	  add_person(&person);
	}

#if 1
	// 昨年の景気ラインと昨年の生産力を比較して、景気ライン > 昨年の生産力 となっていたら、非正規社員を必要分だけ投入する
	int add_person_number;

	if (potential > company.productivity){
	  add_person_number = (int)(potential - company.productivity);
	}
	
	// printf("potential = %f, company.productivity = %f, add_person_number=%d\n",potential, company.productivity, add_person_number);

	for (int i=0; i < add_person_number; i++){
	  PERSON person;

	  person.age = 20; // 若い奴を突っ込むことにする
	  person.position = 0;  // 1:正規社員 0:非正規社員
	  person.productivity = 1.0; // 生産力は"1.0"で固定、かつ年齢とともに成長しない)

	  add_person(&person);
	}

	// 昨年の景気ラインと利益のボーダを比較して 、景気ライン < 利益のボーダ となっていたら、非正規社員を全て即時に解雇する
	
	if (potential - border < 0.0){
	  // ===== PERSON ループを回す========
	  p_person = p_first_person->next;
	  while (p_person != p_last_person){
		
		if (p_person->position == 0){ // 非正規社員
		  if (p_person->age > 25){
			delete_person(p_person);
		  }
		}
		
		p_person = p_person->next;
	  }
	  // ===== PERSON ループを回す(ここまで)========
	}

#endif //0

	potential = juglar_cycles(year) * 1500.0 + 7500.0; // 景気ライン(ジャグラーサイクル)の更新


	/////////// ここから生産力算出ループ

	company.productivity = 0; // 生産力ゼロリセット
	company.payroll = 0 ; // 従業員数ゼロリセット

	// ===== PERSON ループを回す========
	p_person = p_first_person->next;
	while (p_person != p_last_person){
	  
	  company.payroll += 1 ; // 従業員数一人加算
	  company.productivity += p_person->productivity; // 生産力加算
	  
	  p_person = p_person->next;
	}
	// ===== PERSON ループを回す(ここまで)========
	
	//printf("company.payroll = %d\n",company.payroll); // company.payroll = 4000
	//printf("company.productivity = %f\n",company.productivity); // company.productivity = 7437.500000
	
	
	double profit = min(company.productivity, potential) - border;
	sum_profit += profit; 
	
	//printf("year = %d company.productivity = %f potential = %f profit = %f sum_profit=%f payroll=%d\n",year,company.productivity, potential, profit,sum_profit,company.payroll);
	printf("%d,%f,%f,%f,%f,%d\n",year,company.productivity, potential, profit,sum_profit,company.payroll);

	
  } //  for ( int year = 2017; year < 2040; year++){
  
  return 0;
}