Tutorials

Tutorial List


Disqus for Google Sites


React hot reload & Redux (Part 2)

posted Dec 5, 2016, 1:59 AM by Andi Muqsith Ashari   [ updated Dec 5, 2016, 6:56 PM by Surya Wang ]


This is the second part of React hot reload & Redux, in this part we will learn about how to update and deleting product item that inserted using code we have built in the first tutorial, the first thing is modifying actions/index.js by adding updateProduct and deleteProduct method as action:

export const ADD_PRODUCT = 'ADD_PRODUCT';
export const UPDATE_PRODUCT = 'UPDATE_PRODUCT';
export const DELETE_PRODUCT = 'DELETE_PRODUCT';

let nextProductId = 1;

export function addProduct(product)
{
product.id = nextProductId++; 
    return {
        type: ADD_PRODUCT,
        product: product
    };
}

export function updateProduct(product){
return {
type : UPDATE_PRODUCT, 
product: product
}
}

export function deleteProduct(product){
return {
type : DELETE_PRODUCT,
product : product
}
}

Now we have 3 actions, addProduct, updateProduct, and deleteProduct, the next step is creating update and delete reducer by modifying reducer/products.js:

import { ADD_PRODUCT, UPDATE_PRODUCT, DELETE_PRODUCT } from '../actions';

const initialState = {
    products: []
};

export function crudApp(state = initialState, action) {

    switch(action.type)
    {

        case ADD_PRODUCT: {
            return Object.assign({}, state, {
                products: [
                    ...state.products,
                    action.product
                ]
            });
        }

        case UPDATE_PRODUCT:{
            state.products.forEach(function(val,key){
                if(action.product.id==val.id){
                    state.products[key] = action.product;
                }
            });
            return Object.assign({}, state, {
                products: [
                    ...state.products
                ]
            });
        }

        case DELETE_PRODUCT: {
            state.products.forEach(function(val,key){
                if(action.product.id==val.id){
                    state.products.splice(key,1);
                }
            });
            return Object.assign({}, state, {
                products: [
                    ...state.products
                ]
            });
        }

    }

    return state;
}

Our actions and reducers are ready to use, the last thing is importing our new action into our react component and use them as a functionality of our application (Insert, Update and Delete):

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';

import { crudApp } from './reducers/products';
import { addProduct, updateProduct, deleteProduct } from './actions';

var store = createStore(crudApp);

class App extends React.Component {

    constructor(){
        super();
        this.state = {
            products: [],
            product: {
                id : '',
                name : '',
                description : ''
            }
        };
    }

    componentWillMount(){
        store.subscribe(() => {
            var state = store.getState();
            this.setState({
                products: state.products
            });
        });
    }

    handleSubmitForm(e)
    {
        e.preventDefault();
        if(this.state.product.id){
            store.dispatch(updateProduct(this.state.product));
        }else{
            store.dispatch(addProduct(this.state.product));
        }
        this.setState({
            product:{
                id:'',
                name:'',
                description:''
            }
        });
    }

    handleSelectProduct(product,e){
        this.setState({
            product:product
        });
    }

    handleDeleteProduct(product,e){
        store.dispatch(deleteProduct(product));
        this.setState({
            product:{
                id:'',
                name:'',
                description:''
            }
        });
    }

    handleProductNameChange(e){
        this.setState({
            product: {
                id : this.state.product.id,
                name : e.target.value,
                description:this.state.product.description
            },
        });
    }

    handleProductDescriptionChange(e){
        this.setState({
            product: {
                id : this.state.product.id,
                name:this.state.product.name,
                description : e.target.value
            },
        });
    }

    handleProductIdChange(e){
        this.setState({
            product: {
                id : e.target.value,
                name:this.state.product.name,
                description : this.state.product.description
            },
        });
    }   

    render () {

        var mode = "Add Product";
        if(this.state.product.id) mode = "Update Product";

        return(
            <div>
                <p>HMR is cool</p>
                <form onSubmit={this.handleSubmitForm.bind(this)}>
                    <table>
                        <tbody>
                            <tr>
                                <td>Product Id</td>
                                <td><input value={this.state.product.id} disabled="disabled" type="text" onChange={this.handleProductIdChange.bind(this)}/></td>
                            </tr>
                            <tr>
                                <td>Product Name</td>
                                <td><input value={this.state.product.name} type="text" onChange={this.handleProductNameChange.bind(this)}/></td>
                            </tr>
                            <tr>
                                <td>Product Description</td>
                                <td><input value={this.state.product.description} type="text" onChange={this.handleProductDescriptionChange.bind(this)}/></td>
                            </tr>
                        </tbody>
                    </table>
                    <input type="submit" value={mode}/>
                </form>
                <hr />
                <table width="500">
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Name</th>
                            <th>Description</th>
                            <th>Action</th>
                        </tr>
                    </thead>
                    <tbody>
                    {
                        this.state.products.map((product,i)=>{
                            return (
                                <tr key={i}>
                                    <td>{product.id}</td>
                                    <td>{product.name}</td>
                                    <td>{product.description}</td>
                                    <td>
                                        <button onClick={this.handleSelectProduct.bind(this,product)}>Select</button>
                                        <button onClick={this.handleDeleteProduct.bind(this,product)}>Delete</button>
                                    </td>
                                </tr>
                            )
                        })
                    }
                    </tbody>
                </table>
            </div>
        );
    }
};

render(<App/>, document.getElementById('app'));

module.hot.accept();

Now we can inserting, viewing, updating, and deleting our data by using our simple React & Redux application.


Creating a Music Playlist with Visualizer Using HTML5 Web Audio API, Canvas and Node.js

posted Dec 4, 2016, 11:41 PM by Benedictus Jason Reinhart   [ updated Dec 5, 2016, 6:56 PM by Surya Wang ]


The Web Audio API provides a powerful and versatile system for controlling audio on the Web, allowing developers to choose audio sources, add effects to audio, create audio visualizations, apply spatial effects (such as panning) and much more. (Source)

The concept of the Web Audio API is simple. Your audio things are described as node. The source of audio is a node. The effects on the audio (reverb, gain, filters, etc.) is also a node. The output of your audio (speaker, headphones, line out, etc.) is a node. All these nodes are connected as a chain of processes. If we want to give filter to our music, first we need the audio source node, the music node. Then we connect the music node to the filter node, and filter node to the destination node (a speaker or headphone). 

Other than filters and reverb, there is also an AnalyserNode. AnalyserNode can be used to get the information of frequency and time-domain analysis in real-time. Using the frequency information given by the AnalyserNode, we can provide a visualizer drawn in HTML5 canvas.
In terms of code, we need to:
  • Create an AudioContext object as the context or container of all our nodes
  • Create an audio source node
  • Create an AnalyserNode object to analyse the frequency in the audio currently played
  • Connect source to AnalyserNode so it can be analysed
  • Connect AnalyserNode to AudioContext.destination
Firstly, AnalyserNode does not change any frequency in our audio but still output the audio input it takes, so we can safely assume that the input of destination is the same as the output of source. Secondly, an AudioContext has a property destination, it's usually the speaker or headphone of the user.

Before we start, make sure you have Node.js installed with Express in it. You can download Node.js in here and Express.js here.

Now, we start by setting up the server application first. The directory should look like this:
  |-- visualizer
    |-- songs
      |-- song.mp3
    |-- index.html
    |-- app.js

We'll write our server code in app.js. The server access control has to allow our origin, because streaming media files cannot be done by using file:/// URI and not all server in the internet allow cross domain origin.

app.js 
var express = require('express');
var fs = require('fs');
var app = express();

app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
});
app.use(express.static('songs'));

app.get('/', function (req, res) {
    var filenames = fs.readdirSync('./songs/');
    var html = fs.readFileSync('./index.html', {encoding: 'utf-8'});
    var options = '';
    var sources = '';
    for (var i = 0; i < filenames.length; i++) {
        if (filenames[i].endsWith('.mp3')) {
            options += '<option value="'+filenames[i]+'">'+filenames[i]+'</option>';
        }
    }
    var render = html.toString().replace('#select#', '<select>'+options+'</select>');
    res.send(render);
});

app.listen(3000, function () {
    console.log('Visualizer server listening on port 3000');
});

To list all mp3 files in our directory, we need the fs (filesystem) module from Node. Then, we read all the mp3 files in directory and add it as an item in a combo box. Later in index.html we need to write #select# that will be replaced by the server with the combo box filled with mp3 file name items. As the server will send index.html as the response, we should write the index.html now.

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Audio Visualizer</title>
    <style>
        html, body {
            margin: 0;
            width: 100%;
            height: 100%;
            font-family: 'consolas';
            background: #111;
            color: white;
        }
        #control, canvas {
            position: fixed;
            top: 0;
        }
        select {
            position: relative;
            top: -10px;
            padding: 3px;
        }
        #stats {
            position: fixed;
            bottom: 0;
            left: 0;
        }
    </style>
</head>
<body>
    <canvas></canvas>
    <div id="control">
        <audio controls loop autoplay>
        </audio>
        #select#
        <div id="settings">
            <div>Min. Frequency <input type="range" min="20" value="20" step="50" max="20000" name="minFrequency"></div>
            <div>Max. Frequency <input type="range" min="20" value="20000" step="50" max="20000" name="maxFrequency"></div>
            <div>Smoothing <input type="range" min="0" max="0.9" step="0.05" value="0.8"></div>
        </div>
    </div>
    <div id="stats">
    </div>

    <script>
        var control = document.querySelector('#control');

        var settings = document.querySelectorAll('#settings input');
        settings[0].addEventListener('change', changeMinMaxFrequency);
        settings[1].addEventListener('change', changeMinMaxFrequency);
        settings[2].addEventListener('change', changeSmoothness);

        function changeMinMaxFrequency() {
            minFrequency = settings[0].value;
            maxFrequency = settings[1].value;
            minIndex = Math.ceil(minFrequency / frequencyRange)|0;
            maxIndex = Math.floor(maxFrequency / frequencyRange)|0;

            // initialize balls
            balls.length = 0;
            for (var i = minIndex; i < maxIndex; i++) {
                balls[i] = new Ball(canvas.width / 2, canvas.height / 2, 360 / (maxIndex-minIndex) * i);
            }
        }

        function changeSmoothness() {
            analyser.smoothingTimeConstant = settings[2].value;
        }

        function randomBetween(min, max) {
            return (Math.random() * (max - min) + min) | 0;
        }

        class Ball {
            constructor(x, y, angle) {
                this.x = x;
                this.y = y;
                this.angle = angle;
                this.color = 'rgba('+randomBetween(60, 200)+', '+randomBetween(60, 200)+', '+randomBetween(60, 200)+', 1)';
            }
        }
        Ball.radius = 3.5;
        Ball.trailSize = 2;

        var canvas = document.querySelector('canvas'),
            ctx = canvas.getContext('2d'),
            audio = document.querySelector('audio'),
            audioContext = new AudioContext(), // "Container" for all audio nodes
            source = audioContext.createMediaElementSource(audio), // The music, we get it from <audio> element
            analyser = audioContext.createAnalyser(); // To get frequency information of source
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        analyser.fftSize = 1024; // FFT algorithm of signal processing, the size must be a power of 2
        analyser.smoothingTimeConstant = 0.8;

        var bufferLength = analyser.frequencyBinCount, // array of frequencies length, must be half of FFT size
            sampleRate = audioContext.sampleRate, // The audio context sample rate, usually 48000hz or 44100hz. The maximum frequency in the music is half of this sample rate.
            frequencyRange = sampleRate / bufferLength, // each element of the array represents a range of frequencies
            minFrequency = 20, // minimum frequency a human can hear is 20hz, so we set the minimum frequency our visualizer can draw to 20.
            maxFrequency = 20000, // maximum frequency a human can hear varies depending by its age, but normally it's 20,000hz. more information: http://hypertextbook.com/facts/2003/ChrisDAmbrose.shtml
            minIndex, // the lowest index of the minimum frequency
            maxIndex, // the highest index of the maximum frequency
            dataArray = new Uint8Array(bufferLength),
            balls = [];

        changeMinMaxFrequency(); // init balls, min and max frequency
        source.connect(analyser);
        analyser.connect(audioContext.destination);

        var lastTime = Date.now(),
            stats = document.querySelector('#stats');
            updateStatsInterval = 200; // milliseconds
            stats.updateInterval = updateStatsInterval;
        function draw() {
            requestAnimationFrame(draw);
            var now = Date.now();
            var deltaTime = now - lastTime;
            lastTime = now;

            stats.updateInterval -= deltaTime;
            if (stats.updateInterval < 0) {
                stats.innerHTML = 'FPS: ' + ((1000 / deltaTime) | 0);
                stats.updateInterval = updateStatsInterval;
            }

            analyser.getByteFrequencyData(dataArray);

            ctx.clearRect(0, 0, canvas.width, canvas.height);

            for(var i = minIndex; i < maxIndex; i++) {
                var distance = dataArray[i];
                distance *= distance / 200;
                if (distance < 5) continue;
                var angle = balls[i].angle * (Math.PI / 180);
                balls[i].x = Math.cos(angle) * distance;
                balls[i].y = Math.sin(angle) * distance;
                var gradient = ctx.createLinearGradient(canvas.width/2, canvas.height/2, balls[i].x+canvas.width/2, balls[i].y + canvas.height/2);
                gradient.addColorStop(0, "rgb(150,150,150)");
                gradient.addColorStop(1, balls[i].color);
                ctx.strokeStyle = gradient;
                ctx.fillStyle = balls[i].color;

                ctx.beginPath();
                ctx.arc(balls[i].x + canvas.width/2, balls[i].y + canvas.height/2, Ball.radius*distance/100, 0, 2 * Math.PI, false);
                ctx.fill();
                ctx.closePath();

                ctx.beginPath();
                ctx.moveTo(canvas.width/2, canvas.height/2);
                ctx.lineTo(balls[i].x + canvas.width/2, balls[i].y + canvas.height/2);
                ctx.lineWidth = Ball.trailSize * distance/300;
                ctx.stroke();
            }
        };

        draw();

        var select = document.querySelector('select');
        select.addEventListener('change', function() {
            document.querySelector('audio').remove();
            var audio = document.createElement('audio');
            audio.setAttribute('autoplay', 'autoplay');
            audio.setAttribute('loop', 'loop');
            audio.setAttribute('controls', 'controls');
            var src = document.createElement('source');
            src.setAttribute('src', this.value);

            audio.appendChild(src);
            control.prepend(audio);

            analyser.disconnect();
            source.disconnect();

            source = audioContext.createMediaElementSource(audio);
            source.connect(analyser);
            analyser.connect(audioContext.destination);
        });
    </script>
</body>
</html>

First, we need to create the context, source, and analyser.
var audio = document.querySelector('audio'),
    audioContext = new AudioContext(), // "Container" for all audio nodes
    source = audioContext.createMediaElementSource(audio), // The music, we get it from <audio> element
    analyser = audioContext.createAnalyser(); // To get frequency information of source

Then, simply connect those nodes together.
source.connect(analyser);
analyser.connect(audioContext.destination);

FYI, if we don't want any filters or analysis from another node, we can just simply connect the source and destination altogether.
source.connect(audioContext.destination);
that way the audio source output will goes directly to user's speaker/headphone without any alteration or analysis.

The analyser object has several properties. The fftSize is used to determine the FFT algorithm buffer size and it needs to be a power of 2 number.
var bufferLength = analyser.frequencyBinCount; // this will always be half of the fftSize

we use the frequencyBinCount property to get the size of the array, which will be half of the fftSize property of analyser. Knowing the array size, we can declare the unsigned integer array by doing this:
var dataArray = new Uint8Array(bufferLength);

dataArray is used to render the visualizer, it stores the frequency volume value. The visualizer is going to display the frequency volume. For example, a kick drum should be around 20-200hz, so dataArray from [0] to [3] (with a frequencyRange value around 80-90) should have high values depending on how hard the kick drum sounds.

The array will be filled every time the canvas redraw, using this method:
analyser.getByteFrequencyData(dataArray);

With the frequency information every frame, we can transform them into a visualizer. Assuming the frequency range is 80hz, by using loop we can draw a line for each frequency array that represents 80-160hz, 81-160hz, 161-240hz, and so on until it reaches the maximum frequency of 20000. Notice that we also skip the 0-80hz because our minimum frequency is 20hz, so we skip the first index of the array. Remember, each element in the array represents the volume of the frequency range. That means dataArray[20] represents the volume of 1681-1760hz.

To handle audio source change, for example when user wants to change the music, we cannot just disconnect the nodes and change the audio source. That is because the <audio> element is already bound to the context, and as far as I know there is no way (if you know how please tell me in the comment section!) to disconnect the audio element source from AudioContext. A simple solution I found was to just delete the old <audio> element and create another <audio> element with different source.

var select = document.querySelector('select');
select.addEventListener('change', function() {
    querySelector('audio').remove();
    audio = document.createElement('audio');
    audio.setAttribute('autoplay', 'autoplay');
    audio.setAttribute('loop', 'loop');
    audio.setAttribute('controls', 'controls');

    var src = document.createElement('source');
    src.setAttribute('src', this.value);

    audio.appendChild(src);
    control.prepend(audio);

    analyser.disconnect();
    source.disconnect();

    source = audioContext.createMediaElementSource(audio);
    source.connect(analyser);
    analyser.connect(audioContext.destination);
});

Your final result should be like this:



React hot reload & Redux (Part 1)

posted Nov 30, 2016, 10:45 PM by Denny Angkasa   [ updated Dec 5, 2016, 6:55 PM by Surya Wang ]


This tutorial will cover how to setup react hot reload with redux. React hot reload allows us to change the code live without the the page being refreshed, unlike webpack-dev-server that auomatically refreshes the page when the code is changed, hot reload allows the page to load the changes on the fly, preserving the state of the application. We will also set up Redux in this tutorial, using it to manage the state of the application.

We start by creating the file package.json
{
  "name": "reactest",
  "version": "1.0.0",
  "description": "this is a test application",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "Denny Angkasa",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.8",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "express": "^4.14.0",
    "react-hot-loader": "^1.3.1",
    "webpack": "^1.13.3",
    "webpack-dev-middleware": "^1.8.4",
    "webpack-hot-middleware": "^2.13.2"
  },
  "dependencies": {
    "react": "^15.4.1",
    "react-dom": "^15.4.1",
    "react-redux": "^4.4.6",
    "redux": "^3.6.0"
  }
}


We will use the file to manage our dependencies and use scripts for npm. We are dividing the dependencies into 2, devDependencies and dependencies. DevDependencies are dependencies that will be used only in development, while dependencies are needed to execute the application. Babel here will be the transpiler for our JSX & ES6 code to ES5 javascript code. Express will be used as our server to serve static files, with webpack middlewares as the API for the node server to enable hot reloading. Webpack is used to compile our javascript codes into bundles. When this package.json has been created, open a command window in the directory of the package.json file and execute npm install. The package manager will install our dependencies.


Next we will create the node server to serve our static files and hot reloading. create the file server.js. This server will listen on port 3000. As we can see in the server.js, the server needs webpack config and index.html.
var path = require('path');
var webpack = require('webpack');
var express = require('express');
var config = require('./webpack.config');

var app = express();
var compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));

app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});

app.listen(3000, function(err) {
  if (err) {
    return console.error(err);
  }

  console.log('Listening at http://localhost:3000/');
})


Let's create the index.html file.
<html>
  <head>
    <meta charset="utf-8">
    <title>React Application</title>
  </head>
  <body>
    <div id="app" />
    <script src="static/bundle.js" type="text/javascript"></script>
  </body>
</html>
The index.html file expects only a single javascript file, which is the bundle.js in static folder bundled by webpack.


Next will be the webpack.config.js. This is the main configuration file for webpack and is very important for the hot reload to work.
var webpack = require('webpack');
var path = require('path');

var BUILD_DIR = path.resolve(__dirname, 'dist');
var APP_DIR = path.resolve(__dirname, 'src');

var config = {
    devtool: 'inline-source-map',
    entry: [
        'webpack-hot-middleware/client',
        APP_DIR + '/index.jsx'
    ],
    output: {
        path: BUILD_DIR,
        filename: 'bundle.js',
        publicPath: '/static/'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?/,
                include: APP_DIR,
                loaders: ['react-hot', 'babel']
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin
    ]
};

module.exports = config;
In this file we define the directories for build and development source files. We use the inline-source-map devtool to provide easy debugging. Because when javascript files are bundled it is very difficult to debug it without source maps. Inline source map allows us to easily debug our application using browser's development tools as if we are actually debugging our source code without bundling. Entries are used for the webpack entry into our application, and the webpack-hot-middleware/client is used by the server to provide our hot reload. Here we are entering our application through the file index.jsx. The output will be the target location for webpack bundling output, with the filename bundle.js. Modules are used to define which loaders we will used for each file type. Here we state using regex that *.js and *.jsx files will use react-hot loader and babel loader. To enable hot reload we have to use the plugin webpack.HotModuleReplacementPlugin(), while webpack.NoErrorsPlugin ensures that our application won't crash and need to reload when we do syntax error in our code. We then export the config for the server.js to use.


Note that above we use babel loader. We need to create the babel configuration file. Create the file .babelrc
{
    "presets": ["react", "es2015"]
}


Now we have finished configuring our application. Next we will configure our redux components. 2 of the main components of redux are actions and reducers. Actions are functions that doesn't directly alter the application's states. Reducers on the other hand will be the one that updates the state of the application using actions.
Create the folder src in our project folder. Inside the src folder create actions folder. The actions folder will contain all of our actions. In the actions folder create the file index.js.
export const ADD_PRODUCT = 'ADD_PRODUCT';
export const UPDATE_PRODUCT = 'UPDATE_PRODUCT';
export const DELETE_PRODUCT = 'DELETE_PRODUCT';

let nextProductId = 1;
export function addProduct(product)
{
    return {
        type: ADD_PRODUCT,
        product: {
            id: nextProductId++,
            name: product.name,
            description: product.description
        } 
    };
}
For now we will only implement the AddProduct functionality in the action. As you can see, this action will only return a new product with unique id each time it is called, hence not updating the state of the application.


Now we will create the reducer for products. Create the folder reducers in the same directory as actions folder. Inside it we create products.js containing the reducer for products.
import { ADD_PRODUCT, UPDATE_PRODUCT, DELETE_PRODUCT } from '../actions';

const initialState = {
    products: []
};

export function crudApp(state = initialState, action) {

    switch(action.type)
    {
        case ADD_PRODUCT: {
            return Object.assign({}, state, {
                products: [
                    ...state.products,
                    action.product
                ]
            });
        }
    }

    return state;
}
Which is the ES6 equivalent of
import { ADD_PRODUCT, UPDATE_PRODUCT, DELETE_PRODUCT } from '../actions';

const initialState = {
    products: []
};

export function crudApp(state, action) {
    
    if(typeof state === 'undefined')
        state = initialState;

    switch(action.type)
    {
        case ADD_PRODUCT: {
            return Object.assign({}, state, {
                products: [
                    ...state.products,
                    action.product
                ]
            });
        }
    }

    return state;
}


Here we import the action strings from the actions index.js file. We create an initial state of the application, containing empty array of products. This reducer will export the crudApp function that will be used to update the state of the application. When we call this function, we have to pass a state and an action string. For now we will only handle the action for adding a new product. Notice that we are returning a new state if the action is valid, without overriding the passed state.  This allows us to preserve all the state that the application has been in. An example in the ADD_PRODUCT action, we append the products with the new product created by the action.


Now we are gonna create the entry point and the main file of our application. Create the file index.jsx in the same directory as actions and reducers folder.
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';

import { crudApp } from './reducers/products';

import { addProduct } from './actions';

var store = createStore(crudApp);

class App extends React.Component {

    constructor(){
        super();
        this.state = {
            products: [],
            productName: '',
            productDescription: ''
        };
    }

    componentWillMount(){
        store.subscribe(() => {
            var state = store.getState();
            this.setState({
                products: state.products
            });
        });
    }

    handleAddProductClick()
    {
        store.dispatch(addProduct({
            name: this.state.productName,
            description: this.state.productDescription
        }));
    }

    handleProductNameChange(pn){

        this.setState({
            products: this.state.products,
            productName: pn.target.value,
            productDescription: this.state.productDescription
        });
    }

    handleProductDescriptionChange(pd){
        this.setState({
            products: this.state.products,
            productName: this.state.productName,
            productDescription: pd.target.value
        });
    }

    render () {
        return(
            <div>
                <p>HMR is cool</p>
                <table>
                    <tbody>
                        <tr>
                            <td>Product Name</td>
                            <td><input type="text" onChange={this.handleProductNameChange.bind(this)}/></td>
                        </tr>
                        <tr>
                            <td>Product Description</td>
                            <td><input type="text" onChange={this.handleProductDescriptionChange.bind(this)}/></td>
                        </tr>
                    </tbody>
                </table>
                <button onClick={this.handleAddProductClick.bind(this)}>Add product</button>
                <ul>
                {
                    this.state.products.map((product, i) => {
                        return <li key={i}>{product.name} - {product.description}</li>
                    })
                }
                </ul>
            </div>
        );
    }
};

render(<App/>, document.getElementById('app'));

module.hot.accept();
Here we create store by using the createStore method from redux, passing in our crudApp function exported by reducers/products.js. On componentWillMount we subscribe to the store and state what to do when the state is updated. When the state is updated we update the state of the component that will update the view. To run the application just open a command window in the project root folder and execute npm start.


Every time we update the code the HMR will hot reload our application, preserving the state of applications that are not in the files we changed.

Directory structure

- node_modules
- src
    - actions
        - index.js
    - reducers
        - products.js
    - index.jsx
- .babelrc
- index.html
- package.json
- server.js
- webpack.config.js

Creating Simple CRUD MySQL, ExpressJS, and ReactJS

posted Nov 1, 2016, 7:21 PM by Andi Muqsith Ashari   [ updated Dec 5, 2016, 6:55 PM by Surya Wang ]


Introduction

In this tutorial we’re going to build a simple CRUD (Create, Read, Update, and Delete) web application using ExpressJS, React, and MySQL as database.

Preparing the Database

Create database named ‘tutorial’ and create table named ‘products’ inside the database, here is the table details:

Column Name

Data Type

Length

Note

id

int

 

Auto Increment, Primary Key

name

varchar

50

 

price

Int

 

 

created_at

timestamp

 

Default : CURRENT_TIMESTAMP

 

Creating New ExpressJS Project

We need Express Generator to build our Express empty project, type this command in your command line to install Express Generator globally to your system:

npm install express-generator -g

After installing Express Generator, type this command to generate new empty project:
express tutorial

Change the “tutorial” to your project name, ExpressJS will generate a default project structure as shown below: 
 
Then install the dependencies by running this command:
npm install

After installing the dependencies your project is ready to run by running this command:

npm start

If you seeing this command result on your command line, then it’s mean your project is already running at : http://localhost:3000


Creating a Simple CRUD Web Service Using MySQL

We need to install mysql dependency to connect to mysql database, use this command to install mysql dependency:

npm install mysql --save

After install mysql dependency, import and create pool to mysql database by adding this lines of code into app.js:

var mysql = require('mysql');

//connect to mysql don't use var tomake it global

pool = mysql.createPool({

    host: 'localhost',

    user: 'root',

    password: '',

    database: 'tutorial'

});

Change the host, user, password, and database according to your mysql database environment, after creating the pool to mysql database, let’s create file named ‘products.js’ inside routes directory, we are going to create web service with details:

Method

Action

Description

GET

/products

Get list of products

POST

/products

Insert new product data

GET

/products/:id

Get product based on id

PUT

/products/:id

Update product based on id

DELETE

/products/:id

Delete product based on id

Inside the products.js file add this following code:

var express = require('express');

var router = express.Router();


//get list of product

router.get('/', function(req, res, next) {

    pool.getConnection(function(err, connection) {

        connection.query("SELECT * FROM products", function(err, rows) {

            if (!err && rows.length > 0) {

                res.json(rows);

            } else {

                res.json([]);

            }

        });

    });

});


//get product by id

router.get('/:id', function(req, res, next) {

    pool.getConnection(function(err, connection) {

        var id = req.params.id;

        connection.query("SELECT * FROM products WHERE id='" + id + "' LIMIT 1", function(err, rows) {

            if (!err && rows.length > 0) {

                res.json(rows[0]);

            } else {

                res.json([]);

            }

        });

    });

});


//add new product

router.post('/', function(req, res, next) {

    pool.getConnection(function(err, connection) {

        var postBody = req.body;

        var productName = postBody.name;

        var productPrice = postBody.price;

        connection.query("INSERT INTO products (name, price) VALUES ('" + productName + "','" + productPrice + "')", function(err, rows) {

            if (rows.affectedRows) {

                connection.query("SELECT * FROM products WHERE id='" + rows.insertId + "' LIMIT 1", function(err, rows) {

                    if (!err && rows.length > 0) {

                        res.json(rows[0]);

                    } else {

                        res.json([]);

                    }

                });

            }

        });

    });

});


//delete product

router.delete('/:id', function(req, res, next) {

    pool.getConnection(function(err, connection) {

        var id = req.params.id;

        connection.query("DELETE FROM products WHERE id='" + id + "'", function(err, rows) {

            if (!err) {

                res.json({

                    "status": true

                });

            } else {

                res.json([]);

            }

        });

    });

});


//update product

router.put('/:id', function(req, res, next) {

    pool.getConnection(function(err, connection) {

        var postBody = req.body;

        var productId = req.params.id;

        var productName = postBody.name;

        var productPrice = postBody.price;

        connection.query("UPDATE products SET name='" + productName + "', price='" + productPrice + "' WHERE id='" + productId + "'", function(err, rows) {

            if (rows.affectedRows) {

                connection.query("SELECT * FROM products WHERE id='" + productId + "' LIMIT 1", function(err, rows) {

                    if (!err && rows.length > 0) {

                        res.json(rows[0]);

                    } else {

                        res.json([]);

                    }

                });

            }

        });

    });

});


module.exports = router;

Our product service is ready to use, we need to import the products.js into app.js by adding this lines of code in app.js file:

//import the products.js

var products = require('./routes/products');

//add products to ExpressJS route

app.use('/products', products);

You can check your web service using 3rd party application like postman to make sure your web service is running well, don’t forget to restart your project by stopping the project using ctrl+c and run the project again using :

npm start

Creating the Front End Using ReactJS

Our web service is ready to use, next thing to do is installing the ReactJS into your project, we need to use Webpack and Babel dependencies to run ReactJS, use command below to install the dependencies to our project:

npm install webpack –save

npm i babel-loader babel-core babel-preset-es2015 babel-preset-react –save

npm i react react-dom –save

We need to use React Roter to route our React application and Axios to make a http request in our project:

npm install react-router –save

npm install axios --save

After all things already installed lets modify index.jade as project index page, this page will import jQuery, bootstrap, toastr, and bundle.js (application file generated by babel):

doctype html

html

  head

    link(rel="stylesheet",href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css")

    link(rel="stylesheet",href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css")

    script(src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js")

    script(src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js")

    script(src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js")

  body

    #app

    script(src="javascripts/bundle.js")

We need to add webpack.config.js as a webpack configuration file in our root directory:

var webpack = require('webpack');

var path = require('path');


var BUILD_DIR = path.resolve(__dirname, 'public');

var APP_DIR = path.resolve(__dirname, 'app');


var config = {

    entry: APP_DIR + '/index.jsx',

    output: {

        path: BUILD_DIR + '/javascripts',

        filename: 'bundle.js'

    },

    module: {

        loaders: [{

            test: /\.jsx?/,

            include: APP_DIR,

            loader: 'babel'

        }]

    }

}


module.exports = config;

Create directory named app inside our root directory, create index.jsx file inside the app directory:

import React from 'react';

import { render } from 'react-dom';

import { Router, Route, Link, hashHistory } from 'react-router'


import HomeComponent from './components/home/HomeComponent.jsx';

import ProductComponent from './components/product/ProductComponent.jsx';

import ProductListComponent from './components/product/ProductListComponent.jsx';

import ProductNewComponent from './components/product/ProductNewComponent.jsx';

import ProductEditComponent from './components/product/ProductEditComponent.jsx';

import AboutComponent from './components/about/AboutComponent.jsx';


class App extends React.Component {

    

    render() {

        return (

            <div>

                <div className="row">

                    <div className="col-sm-8 col-sm-offset-2">

                        <nav className="navbar navbar-default">

                            <div className="container-fluid">

                                <div className="navbar-header">

                                    <a className="navbar-brand">Simple CRUD</a>

                                </div>

                                <div id="navbar" className="navbar-collapse collapse">

                                    <ul className="nav navbar-nav">

                                        <li><a href="#/">Home</a></li>

                                        <li><a href="#/product">Product Management</a></li>

                                        <li><a href="#/about">About</a></li>

                                    </ul>

                                </div>

                            </div>

                        </nav>

                        <Router history={hashHistory}>

                            <Route path="/" component={HomeComponent} />

                            <Route path="/product" component={ProductComponent}>

                                <Route path="/product/list" component={ProductListComponent} />

                                <Route path="/product/new" component={ProductNewComponent} />

                                <Route path="/product/edit/:productId" component={ProductEditComponent} />

                            </Route>

                            <Route path="/about" component={AboutComponent} />

                        </Router>

                    </div>

                </div>

            </div>

        );

    }


}


render( <App /> , document.getElementById('app'));

Create 2 directories classes and components inside app directory, and add new file named TableClass.jsx inside classes directory:

import React from 'react';


const TableClass = React.createClass({


    bindHeader:function(){

        var cols = this.props.cols;

        var elements = cols.map(function(colHeader){

            return <th key={colHeader.key}>{colHeader.label}</th>

        });

        //action column

        elements.push(<th key="dataHeaderAction">Action</th>);

        return elements;

    },


    bindData:function(){

        var data = this.props.data;

        var self = this;

        return data.map(function(colData){

            var rowElement = [];

            $.each(colData,function(key,val){

                rowElement.push(<td key={key}>{val}</td>);              

            });

            //action column

            rowElement.push(

                <td key="actionColumn">

                    <div className="col-md-2">

                        <button className="btn btn-xs btn-primary" onClick={self.props.onUpdate} data-id={colData.id}>Edit</button>

                    </div>

                    <div className="col-md-2">

                        <button className="btn btn-xs btn-danger" onClick={self.props.onDelete} data-id={colData.id}>Delete</button>

                    </div>

                </td>

            );

            return <tr className='data-row' data-id={colData.id} key={colData.id}>{rowElement}</tr>;

        });

    },


    render:function(){


        var tableHeader = this.bindHeader();

        var tableData = this.bindData();


        return (

            <table className='table table-hover table-striped table-bordered'>

                <thead>

                    <tr>{tableHeader}</tr>

                </thead>

                <tbody>

                    {tableData}

                </tbody>

            </table>

        );

    }


});


export default TableClass;

Create 3 directories about, home, and product inside components directory, add new file named AboutComponent.jsx inside about directory:

import React from 'react';


class AboutComponent extends React.Component{


    render(){

        return (

            <div className="panel panel-default">

                <div className="panel-heading">About</div>

                <div className="panel-body">

                    This is a simple CRUD tutorial result, this project using : 

                    <ul>

                        <li>NodeJS</li>

                        <li>Express JS</li>

                        <li>MySQL</li>

                        <li>Webpack</li>

                        <li>Babel</li>

                        <li>React</li>

                        <li>Axios</li>

                        <li>jQuery</li>

                        <li>Bootstrap</li>

                        <li>Toastr</li>

                    </ul>

                </div>

            </div>

        );

    }


}


export default AboutComponent;


Add new file named HomeComponent.jsx inside home directory:

import React from 'react';

class HomeComponent extends React.Component{

    render(){
        return (
            <div className="panel panel-default">
                <div className="panel-heading">Home</div>
                <div className="panel-body">Welcome to simple CRUD tutorial</div>
            </div>
        );
    }

}

export default HomeComponent;

The main part of this tutorial is we are going to create product management page inside product directory, we need to create 4 files ProductComponent.jsx, ProductEditComponent.jsx, ProductListComponent.jsx, and ProductNewComponent.jsx

ProductComponent.jsx

import React from 'react';

import { Link } from 'react-router'


class ProductComponent extends React.Component{


    render(){

        return (

            <div className="panel panel-default">

                <div className="panel-heading">Product Management</div>

                <div className="panel-body">

                    <ul className="nav nav-tabs">

                        <li><Link activeClassName="active" to="product/list">Product List</Link></li>

                        <li><Link activeClassName="active" to="product/new">Add New Product</Link></li>

                    </ul>

                    <br />

                    {this.props.children}

                </div>

            </div>

        );

    }


}


export default ProductComponent;

ProductListComponent.jsx

import React from 'react';

import axios from 'axios';


import TableClass from '../../classes/TableClass.jsx';


class ProductListComponent extends React.Component{


    constructor(props) {

        super(props);

        this.getProductList();


        this.state = {

            cols : [

                {key:'dataHeaderId',label:'Id'},

                {key:'dataHeaderName',label:'Name'},

                {key:'dataHeaderPrice',label:'Price'},

                {key:'dataHeaderCreatedAt',label:'Created At'}

            ],

            data : [

                

            ]

        };

    }


    getProductList(){

        toastr.info('Fetching product list...');

        var self = this;

        axios.get('products').then(function(response){

            toastr.clear();

            self.setState({

                cols : self.state.cols,

                data : response.data

            });

        }).catch(function(error){

            toastr.clear();

            toastr.error(error);

        });

    }


    updateProduct(data){

        var id = $(data.target).data('id');

        location.href='#/product/edit/'+id;

    }


    deleteProduct(data){

        var id = $(data.target).data('id');

        $("#deleteConfirmationModal").modal('show');

        $("#deleteButton").attr('data-id',id);

    }


    doDeleteProduct(data){

        var id = $(data.target).data('id');

        toastr.info('Deleting product...');

        axios.delete('products/'+id).then(function(response){

            toastr.clear();

            $(".data-row[data-id='"+id+"']").slideUp();

        }).catch(function(error){

            toastr.clear();

            toastr.error(error);

        });

    }


    render(){

        return (

            <div>

                <TableClass cols={this.state.cols} data={this.state.data} onDelete={this.deleteProduct} onUpdate={this.updateProduct}/>

                <div className="modal fade" id="deleteConfirmationModal" role="dialog">

                    <div className="modal-dialog">

                        <div className="modal-content">

                            <div className="modal-header">

                                <button type="button" className="close" data-dismiss="modal">&times;</button>

                                <h4 className="modal-title">Delete Item</h4>

                            </div>

                            <div className="modal-body">

                                <p>Are you sure want to delete this item?</p>

                            </div>

                            <div className="modal-footer">

                                <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>

                                <button type="button" className="btn btn-danger" 

                                        id="deleteButton" 

                                        onClick={this.doDeleteProduct.bind(this)} 

                                        data-dismiss="modal">Delete Item</button>

                            </div>

                        </div>

                    </div>

                </div>

            </div>

        );

    }


}


export default ProductListComponent;

ProductNewComponent.jsx

import React from 'react';

import axios from 'axios';


class ProductNewComponent extends React.Component{


    constructor(props) {

        super(props);

        this.state = {

            name : null,

            price : null

        }

    }


    submitForm(event){

        event.preventDefault();

        var data = $(event.target).serialize();

        toastr.clear();

        var isError = false;

        if(this.state.name===""){

            toastr.error("Product name must be filled!");

            isError=true;

        }

        if(this.state.price===0 || this.state.price===''){

            toastr.error("Product price must be filled!");

            isError=true;

        }

        if(!isError){

            toastr.info('Inserting new product data...');

            axios.post('/products',{

                name : this.state.name,

                price : this.state.price

            }).then(function(response){

                toastr.clear();

                location.href = "#/product/list";

            }).catch(function(error){

                toastr.clear();

                toastr.error(error);

            });

        }

    }


    onProductNameChange(e){

        this.setState({

            id : this.state.id,

            name : e.target.value.trim(),

            price : this.state.price

        });

    }


    onProductPriceChange(e){

        this.setState({

            id : this.state.id,

            name : this.state.name,

            price : e.target.value

        });

    }


    render(){

        return (

            <div>

                <form className="form-horizontal" onSubmit={this.submitForm.bind(this)}>

                    <div className="form-group">

                        <label className="control-label col-sm-2" htmlFor="productEmail">Name : </label>

                        <div className="col-sm-10">

                            <input  type="text" name='productName' 

                                    onChange={this.onProductNameChange.bind(this)}

                                    id="productName" className="form-control" placeholder="Product Name" />

                        </div>

                    </div>

                    <div className="form-group">

                        <label className="control-label col-sm-2" htmlFor="productPrice">Price : </label>

                        <div className="col-sm-10">

                            <input  type="number" name='productPrice' 

                                    onChange={this.onProductPriceChange.bind(this)}

                                    id="productPrice" className="form-control" placeholder="Product Price" />

                        </div>

                    </div>

                    <div className="form-group">

                        <div className="col-sm-offset-2 col-sm-10">

                            <button type="submit" className="btn btn-default">Save</button>

                        </div>

                    </div>

                </form>


            </div>

        );

    }


}


export default ProductNewComponent;

ProductEditComponent.jsx

import React from 'react';

import axios from 'axios';


class ProductNewComponent extends React.Component{


    constructor(props) {

        super(props);

        this.state = {

            name : null,

            price : null

        }

    }


    submitForm(event){

        event.preventDefault();

        var data = $(event.target).serialize();

        toastr.clear();

        var isError = false;

        if(this.state.name===""){

            toastr.error("Product name must be filled!");

            isError=true;

        }

        if(this.state.price===0 || this.state.price===''){

            toastr.error("Product price must be filled!");

            isError=true;

        }

        if(!isError){

            toastr.info('Inserting new product data...');

            axios.post('/products',{

                name : this.state.name,

                price : this.state.price

            }).then(function(response){

                toastr.clear();

                location.href = "#/product/list";

            }).catch(function(error){

                toastr.clear();

                toastr.error(error);

            });

        }

    }


    onProductNameChange(e){

        this.setState({

            id : this.state.id,

            name : e.target.value.trim(),

            price : this.state.price

        });

    }


    onProductPriceChange(e){

        this.setState({

            id : this.state.id,

            name : this.state.name,

            price : e.target.value

        });

    }


    render(){

        return (

            <div>

                <form className="form-horizontal" onSubmit={this.submitForm.bind(this)}>

                    <div className="form-group">

                        <label className="control-label col-sm-2" htmlFor="productEmail">Name : </label>

                        <div className="col-sm-10">

                            <input  type="text" name='productName' 

                                    onChange={this.onProductNameChange.bind(this)}

                                    id="productName" className="form-control" placeholder="Product Name" />

                        </div>

                    </div>

                    <div className="form-group">

                        <label className="control-label col-sm-2" htmlFor="productPrice">Price : </label>

                        <div className="col-sm-10">

                            <input  type="number" name='productPrice' 

                                    onChange={this.onProductPriceChange.bind(this)}

                                    id="productPrice" className="form-control" placeholder="Product Price" />

                        </div>

                    </div>

                    <div className="form-group">

                        <div className="col-sm-offset-2 col-sm-10">

                            <button type="submit" className="btn btn-default">Save</button>

                        </div>

                    </div>

                </form>


            </div>

        );

    }


}


export default ProductNewComponent;


Creating 3D Dodge Ball Game Using three.js

posted Oct 30, 2016, 11:44 PM by Benedictus Jason Reinhart   [ updated Dec 5, 2016, 6:55 PM by Surya Wang ]


Getting Started

Three.js is a JavaScript library used to create and display animated 3D computer graphics in a web browser. It uses WebGL as the base technology. In this tutorial we are going to create a 3D dodge ball game using the Three.js library and play it a web browser. This tutorial will not cover the basic JavaScript, WebGL, or how to create a multiplayer game.

Before reading this tutorial, make sure you have:

  • ·         Strong basic knowledge of JavaScript
  • ·         Common sense in 3D world (Vectors, Rays, etc.)
  • ·         Text editor for your JavaScript and HTML code (Sublime, Notepad++, VS Code, etc.)
  • ·         Modern browser that supports WebGL (Chrome, Firefox, etc.) to see and test your creation

If you already have those mentioned above, then you should download the Three.js library from here. Before you can use it, you need a HTML to display it, as it’s based on a canvas element.

<!DOCTYPE html> 
<html>
<head> 
    <title>Dodge Ball</title> 
    <style> 
        body { margin: 0; } 
        canvas { width: 100%; height: 100% } 
    </style> 
</head> 
<body> 
    <script src="three.js"></script> 
    <script> 
        // Our JavaScript will go here. 
    </script> 
</body> 
</html>

Creating a Scene

A scene is simply a world where objects “live”. An object such as a cube or sphere needs to be placed in a scene in order to be rendered.

"use strict";
var renderer = null;
var scene = null;
var camera = null;

var balls = [];
var player = null;
var ballsDodged = 0;

var moveLeft = false;
var moveRight = false;

var delta = 0;
var lastUpdate = Date.now();
var spawnTimer = 500; // Delay spawning of the first ball

var init = function() {
    scene = new THREE.Scene(); // Creating a scene.
    camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); // A scene needs a camera so the player can see things in the scene.

    renderer = new THREE.WebGLRenderer(); // The WebGLRenderer of the browser
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement); // It appends a canvas element to the HTML

    // We are going to create the ground using a box. Creating an object (a mesh, actually) in Three.js needs 2 parameter, shape (we call it geometry here) and material (texture, color, etc.)
    var landGeometry = new THREE.BoxGeometry(40, 1, 70);
    var landTexture = new THREE.TextureLoader().load('textures/land.jpg');
    landTexture.wrapS = THREE.RepeatWrapping;
    landTexture.wrapT = THREE.RepeatWrapping;
    landTexture.repeat.set(2, 2);
    var landMaterial = new THREE.MeshLambertMaterial({
        color: 0xaadd00,
        map: landTexture
    });
    var land = new THREE.Mesh(landGeometry, landMaterial);
    scene.add(land); // Add the land to the scene. Without this method, your land will not be rendered.

    var directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);
    scene.add(directionalLight);

    camera.position.set(-0.3, 9, 40); // x, y, z accordingly
}

var render = function() {
    requestAnimationFrame(render);
    var now = Date.now();
    delta = now - lastUpdate;
    lastUpdate = now;
}

init();
render();

In the above code, we create a world (scene), a camera in it so player can see things, a renderer for the web browser, and a directional light so the terrain won’t be dark. Notice that we load a texture (textures/land.jpg). We can apply a texture for a mesh with the TextureLoader class, so make sure you have a texture in textures/ directory.

Summary:

  • ·         Every Three.js application needs a scene as its world (THREE.Scene)
  • ·         Every scene needs a renderer and a camera to be able to see what is in the world (THREE.WebGLRenderer and THREE.PerspectiveCamera, there are other types of camera but in this tutorial we use the PerspectiveCamera)
  • ·         Every object (mesh) in the world (scene) needs a geometry (THREE.BoxGeometry, there are various types of geometry) and material (THREE.MeshLambertMaterial, there are various types of material)
  • ·         Every object instantiated needs to be added to the scene (via scene.add(object))
  • ·         A scene needs lighting so the textures will have brightness on it and shadows (we’ll get into it in later chapter)
  • ·         We need to call requestAnimationFrame(callback) function. The callback parameter is a function to animate and update things every frame
  • ·         We need to compute delta time in order to maintain a stable game as different CPU might give different delta time

Note: If you have an error XmlHttpRequest not allowed to load file, you need a web server to host it. It is not mandatory to use a texture in this tutorial, so you can just skip or comment the LoadTexture code and the map attribute on material.

Adding Player Control

For the sake of simplicity, the player will control a cube that can only strafe left or right. Previously, we have declared:

var player = null;

Before we assign a value to the player, let’s create the object first. Add the following code in the init function:

var playerGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
var playerMaterial = new THREE.MeshLambertMaterial({color: 0x4444dd});
var player = new THREE.Mesh(playerGeometry, playerMaterial);
player.receiveShadow = true;
player.castShadow = true;
player.position.set(0, 1.5, 28);
player.isAlive = true;

scene.add(player);

To make things look realistic, we’ll set the player to receive and cast shadow. We also set the player position to a certain coordinate. Because I hardcoded the values, If you want to use different values for the ground and player, you’ll need to adjust things yourself. Don’t forget to add the player to the scene or the player will not be rendered. The isAlive property is a custom property we use to determine whether the player is alive or not. Later we’ll set it to false if the player collides with a ball, ending the game respectively.

For player to be able to move, we need to add keyboard listeners. Add the following code to the init function:

document.onkeydown = function(event) {
    event = event || window.event;

    switch (event.keyCode) {
    case 37:
    case 65:
        moveLeft = true;
        break;
    case 39:
    case 68:
        moveRight = true;
        break;
    }
}

document.onkeyup = function(event) {
    event = event || window.event;

    switch (event.keyCode) {
    case 37:
    case 65:
        moveLeft = false;
        break;
    case 39:
    case 68:
        moveRight = false;
        break;
    }
}

We have declared the moveLeft and moveRight variable previously, and we’ll set it to true or false according to user’s keyboard state. In this case, the keycode I used are A and left arrow to move left and D and right arrow to move right. Then, add the following code in the render() function so the movement will be checked in every frame:

if (moveLeft && player.position.x > -18) player.translateX(-0.01 * delta);
if (moveRight && player.position.x < 18) player.translateX(0.01 * delta);

The hardcoded width of the ground is 40, and the hardcoded player box width is 1, so we need to validate the box to not move beyond the ground’s width. As the x axis of the ground is 0, the ground will span from -20 to 20, so we validate that the box will not move beyond that point.

Spawning and Rolling Balls

In the previous section we have added a computation of delta time (var delta = ...) and a spawn timer. We will use both the spawn timer variable and the delta time to produce balls for every given time.

Before spawning the balls, let’s prepare a function that creates a ball every time it’s called. We will name it createBall(). Add the function outside from any scope so it can be called anywhere:

var createBall = (function() {
    var ballGeometry = new THREE.SphereGeometry(1, 128, 128);
    var ballTexture = new THREE.TextureLoader().load('textures/ball.jpg');
    ballTexture.wrapS = THREE.RepeatWrapping;
    ballTexture.wrapT = THREE.RepeatWrapping;
    var ballMaterial = new THREE.MeshLambertMaterial({map: ballTexture});

    return function() {
        var ball = new THREE.Mesh(ballGeometry, ballMaterial);
        ball.castShadow = true;
        ball.receiveShadow = true;
        ball.position.set((Math.random() - 0.5) * 38, 1.4, -35);
        return ball;
    }
})();

The main reason we create the function and the self-invoking function is because creating a new geometry, texture, and material are an expensive operation while these are actually reusable objects, so we will store those in the self-invoking function variables.

To spawn a ball for every given time, add the following code in the render() function:

spawnTimer -= delta;
if (spawnTimer <= 0) {
    var ball = createBall();
    scene.add(ball);
    balls.push(ball);
    spawnTimer = 100;
}

The balls variable is used to store the balls so we don’t have to use findObjectByName(). Now, rolling the ball and removing it after reaching certain point:

for (var ball of balls) { 
    var distance = 0.02 * delta; 
    ball.position.z += distance; 
    ball.rotation.x += distance; 

    if (ball.position.z >= 70) { 
        balls.splice(balls.indexOf(ball), 1); 
        scene.remove(ball); 
        ballsDodged++; 
    } 
}

Checking Collision

Collision is an important feature in this game, as the primary goal of the game is to avoid collision. To check whether 2 objects collide each other or not, we will use a simple collision detection using raycaster.

Raycasting can be described as the process of shooting a ray or line from a point to a certain direction. After raycasting, we detect whether the ray intersects with an object or not. In this game, we will cast a ray from player to 8 point, north, north east, east, south east, and so on. If one of these rays intersects with a ball, the player lose the game. To do so, we must check the collision of each ball in the game. Let’s modify our player code to this:

var playerGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5); 
var playerMaterial = new THREE.MeshLambertMaterial({color: 0x4444dd}); 
var directions = [
    new THREE.Vector3(0, 0, 1), 
    new THREE.Vector3(1, 0, 1), 
    new THREE.Vector3(1, 0, 0), 
    new THREE.Vector3(1, 0, -1), 
    new THREE.Vector3(0, 0, -1), 
    new THREE.Vector3(-1, 0, -1), 
    new THREE.Vector3(-1, 0, 0), 
    new THREE.Vector3(-1, 0, 1) 
]; 

var player = new THREE.Mesh(playerGeometry, playerMaterial); 
player.receiveShadow = true; 
player.castShadow = true; 
player.position.set(0, 1.5, 28); 
player.isAlive = true; 
player.checkCollision = function(ball) { 
    var playerPosition = this.position.clone(); 
    var raycaster = new THREE.Raycaster(); 
    var collisions = []; 
    for (var direction of directions) { 
        raycaster.set(playerPosition, direction); 
        var collision = raycaster.intersectObject(ball); 
        if (collision.length > 0 && collision[0].distance <= 0.5) { 
            this.isAlive = false;
            break;
        }
    }
}

So we add a directions array which contains a vector to define the 8 directions mentioned above. We also add a checkCollision() function which has a parameter of ball. Now, we will use the function inside the game loop. Modify the balls update logic:

for (var ball of balls) { 
    var distance = 0.02 * delta; 
    ball.position.z += distance; 
    ball.rotation.x += distance; 
    player.checkCollision(ball); 

    if (ball.position.z >= 70) { 
        balls.splice(balls.indexOf(ball), 1); 
        scene.remove(ball); 
        ballsDodged++;
    }
}

Now we add a validation so the game only runs when the player is alive.

if (player.isAlive) {
    requestAnimationFrame(render);
}

Lighting and Shadow

Most 3D games look realistic with the help of shadow. In Three.js, all objects don’t cast or receive shadows by default. To enable it, we can simply modify the castShadow and receiveShadow attribute to true. Let’s modify our objects now by adding the following code in the init() function:

land.receiveShadow = true;
land.castShadow = false;
player.receiveShadow = true;
player.castShadow = true;

In the createBall:

var ball = new THREE.Mesh(ballGeometry, ballMaterial); 
... 
... 
ball.castShadow = true; 
ball.receiveShadow = true;

Then we add a SpotLight as the “sun” in our scene. Add the SpotLight in init():

var light = new THREE.SpotLight(0xffffff);
light.castShadow = true;
light.angle = Math.PI / 4;
light.position.set(-10, 20, 40);
scene.add(light);

That's it, our game is complete and looks okay now. This tutorial does not cover the best practices of using three.js, so if you have questions, suggestions, and critics please leave a comment below.

[Java] Tic Tac Toe AI with Minimax Tree and Alpha Beta Pruning

posted Oct 28, 2016, 3:53 AM by Denny Angkasa   [ updated Dec 5, 2016, 6:54 PM by Surya Wang ]


Introduction

Tic Tac Toe is a game where players take turns marking spaces in a 3x3 grid with their own symbol, either X or O. It is however, a flawed game in the meaning that when both players do not make a mistake, the game will for sure lead to a draw. In that case, it is possible to create an AI to play the game where it will never lose, only knowing how to win or draw the game. We will create the game and the AI in Java, console based.

Node Class

Node class is used to represent a node in the Minimax Tree. The board variable represents the current state of the board in the node. The nextPlayer variable represents the next player taking the turn. The parent variable represents the parent of the current node in the tree. The heuristic value represents the estimated value of the move of the node. The atDepth variable ensures that we only take a possible next move from atDepth == 1 (next move for the node). The traversalDepth variable represents the depth of the node in the tree.

public class Node {

String[][] board;
String nextPlayer;
Node parent;
int heuristicValue = 0;
int atDepth = 0;
int traversalDepth = 0;
public Node(){
board = new String[3][3];
parent = null;
}
public Node(String[][] board, Node parent, int heuristicValue, int atDepth, String nextPlayer, int traversalDepth){
this.board = board;
this.parent = parent;
this.heuristicValue = heuristicValue;
this.atDepth = atDepth;
this.nextPlayer = nextPlayer;
this.traversalDepth = traversalDepth;
}
}

Starting the game

We create a root node. A root node is the state of the board when the game begins (clean board). We start the game by asking if the player wants to move first. After this we’ll look at how to perform a move for the player.

int getIntInput()
{
    int i = -1;
    try{
        i = scanner.nextInt();
        scanner.nextLine();
    }catch(Exception e){
        scanner.nextLine();
    }
    
    return i;
}

boolean isPlayerMovingFirst(Node node)
{
    printBoard(node);
    String input = "";
    
    do
    {
        System.out.println("Do you want to move first? [y/n] ['e' to exit]: ");
        input = scanner.nextLine();
        
        if(input.equalsIgnoreCase("e"))
            System.exit(0);
    }while(!input.equalsIgnoreCase("y") && !input.equalsIgnoreCase("n"));
    
    if(input.equalsIgnoreCase("y"))
        return true;
    
    return false;
}

void startGame()
{
    Node root = new Node();
    clear();
    
    boolean playerMovesFirst = isPlayerMovingFirst(root);
    if(playerMovesFirst)
    {
        root.nextPlayer = "O";
        moveForPlayer(root);
    }
    else
    {
        root.nextPlayer = "X";
        moveForBot(root);
    }
}

public Main(){
    while(true){
        startGame();
        scanner.nextLine();
    }
}

public static void main(String[] args) {
    new Main();
}

Player’s move

To do a move for the player, we ask the player where they want to move. Then we have to validate if that move is valid. A move is valid if it is inside the board, and the place where the move will be placed in the board is not occupied. If the move is indeed valid, we produce a new node which is the successor of the previous node with the move the player just made. This new node with updated game state is then passed into the moveForBot function so that the bot can move. The bot will then produce a new successor node too and pass it to moveForPlayer function. This cycle will continue until the game ends in either the bot’s win or the bot’s draw, because apparently this bot cannot lose.


ArrayList<Point> getAvailableMoves(Node node)
{
    ArrayList<Point> availableMoves = new ArrayList<Point>();
    for(int i = 0; i<3; i++)
    {
        for(int j = 0; j<3; j++)
        {
            if(node.board[i][j] == null)
            {
                availableMoves.add(new Point(j, i));
            }
        }
    }
    
    return availableMoves;
}

Node getSuccessor(Node node, Point p) {
    
    if(isLeafNode(node)) return null;
    
    return new Node(updateBoard(node, p), node, evaluateHeuristicValue(node), node.atDepth+1, node.nextPlayer.equals("X") ? "O" : "X", node.traversalDepth+1);
}

void printBoard(Node node){
    String board[][] = node.board;
    
    clear();
    System.out.println("==========================");
    System.out.println("| Impossible Tic-Tac Toe |");
    System.out.println("==========================");
    
    System.out.println("-----------");
    System.out.println("|y\\x| 0 | 1 | 2 |");
    
    for(int i = 0; i<3; i++)
    {
        System.out.println("-----------");
        System.out.print("| " + i + " ");
        for(int j = 0; j<3; j++)
        {
            System.out.print("| ");
            if(board[i][j] != null)
                System.out.print(board[i][j]);
            else
                System.out.print(" ");
            System.out.print(" ");
        }
        System.out.println("|");
    }
    System.out.println("-----------");
}

void moveForPlayer(Node node)
{
    Node newNode = new Node();
    ArrayList<Point> availableMoves = getAvailableMoves(node);
    Point input = getInputFromPlayer(node, availableMoves);
    newNode = getSuccessor(node, input);
    
    printBoard(newNode);
    System.out.println("Computer's turn, press enter to continue");
    
    if(checkWin(newNode))
    {
        System.out.println("You Won! Press enter to play again");
    }
    else if(isLeafNode(newNode))
    {
        System.out.println("Draw Game! Press enter to play again");
    }
    else
    {
        scanner.nextLine();
        moveForBot(newNode);
    }
}

Point getInputFromPlayer(Node node, ArrayList<Point> availableMoves)
{
    Point move = new Point();
    do
    {
        int inputX, inputY;
        
        do
        {
            System.out.print("Input X: [0-2]: ");
            inputX = getIntInput();
        }while(inputX < 0 || inputX > 2);
        
        do
        {
            System.out.print("Input Y: [0-2]: ");
            inputY = getIntInput();
        }while(inputY < 0 || inputY > 2);
        
        move.x = inputX;
        move.y = inputY;
        
        if(!isValidMove(move, availableMoves))
            System.out.println("Your move is invalid!");
        
    }while(!isValidMove(move, availableMoves));
    
    return move;
}

Checking if the game has been won by a player or drawn

A player wins the game if he has 3 consecutive symbols in the board either vertically, horizontally, or diagonally. If a node is a leaf node, that means the next player has no possible moves left in the board (board is full), meaning the game ends in a draw.

boolean isLeafNode(Node node){
    return checkWin(node) || getAvailableMoves(node).size() == 0;
}

boolean checkWin(Node node){
    return checkWinRow(node) || checkWinColumn(node) || checkWinDiagonal(node);
}

boolean checkWinRow(Node node){
    
    String[][] board = node.board;
    return (board[0][0] != null && board[0][0] == board[0][1] && board[0][1] == board[0][2]) ||
            (board[1][0] != null && board[1][0] == board[1][1] && board[1][1] == board[1][2]) ||
            (board[2][0] != null && board[2][0] == board[2][1] && board[2][1] == board[2][2]);
}

boolean checkWinColumn(Node node){
    
    String[][] board = node.board;
    return (board[0][0] != null && board[0][0] == board[1][0] && board[1][0] == board[2][0]) ||
            (board[0][1] != null && board[0][1] == board[1][1] && board[1][1] == board[2][1]) ||
            (board[0][2] != null && board[0][2] == board[1][2] && board[1][2] == board[2][2]);
}

boolean checkWinDiagonal(Node node){
    
    String[][] board = node.board;
    return (board[0][0] != null && board[0][0] == board[1][1] && board[1][1] == board[2][2]) ||
            (board[0][2] != null && board[0][2] == board[1][1] && board[1][1] == board[2][0]);
}

The Minimax Tree and Alpha Beta Pruning algorithm

Minimax tree is a tree in which it represents 2 players. It alternates the represented player in each depth. For examples depth 1 represents player, depth 2 represents the bot, depth 3 represents the player, depth 4 represents the bot, and so on. It assumes that each player wants to take the move that is best for them. It will predict a certain number of turns ahead, so that it can produce a move that is an evaluation of the moves the player will take. The algorithm will take the best possible move to ensure that the player does not win the game. It evaluates which move is the best by their heuristic values. For examples a bad move in which player wins has the heuristic value of -1, meaning the bot must not take this move while a good move in which the bot prevents the player from winning (reaching 3 consecutive symbols) has the heuristic value of 1.

Then comes the alpha beta pruning implementation. As I explained in the paragraph above, the bot predicts a certain number of turns ahead, until either the MAX_TRAVERSAL_DEPTH is reached, or it reaches a leaf node. However, when a node already has a bad move in which for example it allows the player to win, we actually do not need to further evaluate the successor of this node since the bot will never take this move. Another case is that the move that has been evaluated is better than the move we are going to evaluate next. Alpha beta pruning allows us to prune the moves that we will not need to evaluate, optimizing the minimax tree algorithm. Below will be given the full code of the game.

The full code

import java.awt.Point;
import java.util.ArrayList;
import java.util.Scanner;


public class Main {
public static final int INFINITY = 99999;
public static final int MAX_TRAVERSAL_DEPTH = 6;
Scanner scanner = new Scanner(System.in);
int getIntInput()
{
int i = -1;
try{
i = scanner.nextInt();
scanner.nextLine();
}catch(Exception e){
scanner.nextLine();
}
return i;
}
void clear()
{
for(int i = 0; i<25; i++)
System.out.println();
}
public Main(){
while(true){
startGame();
scanner.nextLine();
}
}
void startGame()
{
Node root = new Node();
clear();
boolean playerMovesFirst = isPlayerMovingFirst(root);
if(playerMovesFirst)
{
root.nextPlayer = "O";
moveForPlayer(root);
}
else
{
root.nextPlayer = "X";
moveForBot(root);
}
}
boolean isPlayerMovingFirst(Node node)
{
printBoard(node);
String input = "";
do
{
System.out.println("Do you want to move first? [y/n] ['e' to exit]: ");
input = scanner.nextLine();
if(input.equalsIgnoreCase("e"))
System.exit(0);
}while(!input.equalsIgnoreCase("y") && !input.equalsIgnoreCase("n"));
if(input.equalsIgnoreCase("y"))
return true;
return false;
}
void moveForPlayer(Node node)
{
Node newNode = new Node();
ArrayList<Point> availableMoves = getAvailableMoves(node);
Point input = getInputFromPlayer(node, availableMoves);
newNode = getSuccessor(node, input);
printBoard(newNode);
System.out.println("Computer's turn, press enter to continue");
if(checkWin(newNode))
{
System.out.println("You Won! Press enter to play again");
}
else if(isLeafNode(newNode))
{
System.out.println("Draw Game! Press enter to play again");
}
else
{
scanner.nextLine();
moveForBot(newNode);
}
}
Point getInputFromPlayer(Node node, ArrayList<Point> availableMoves)
{
Point move = new Point();
do
{
int inputX, inputY;
do
{
System.out.print("Input X: [0-2]: ");
inputX = getIntInput();
}while(inputX < 0 || inputX > 2);
do
{
System.out.print("Input Y: [0-2]: ");
inputY = getIntInput();
}while(inputY < 0 || inputY > 2);
move.x = inputX;
move.y = inputY;
if(!isValidMove(move, availableMoves))
System.out.println("Your move is invalid!");
}while(!isValidMove(move, availableMoves));
return move;
}
boolean isLeafNode(Node node){
return checkWin(node) || getAvailableMoves(node).size() == 0;
}
boolean checkWin(Node node){
return checkWinRow(node) || checkWinColumn(node) || checkWinDiagonal(node);
}
boolean checkWinRow(Node node){
String[][] board = node.board;
return (board[0][0] != null && board[0][0] == board[0][1] && board[0][1] == board[0][2]) ||
(board[1][0] != null && board[1][0] == board[1][1] && board[1][1] == board[1][2]) ||
(board[2][0] != null && board[2][0] == board[2][1] && board[2][1] == board[2][2]);
}
boolean checkWinColumn(Node node){
String[][] board = node.board;
return (board[0][0] != null && board[0][0] == board[1][0] && board[1][0] == board[2][0]) ||
(board[0][1] != null && board[0][1] == board[1][1] && board[1][1] == board[2][1]) ||
(board[0][2] != null && board[0][2] == board[1][2] && board[1][2] == board[2][2]);
}
boolean checkWinDiagonal(Node node){
String[][] board = node.board;
return (board[0][0] != null && board[0][0] == board[1][1] && board[1][1] == board[2][2]) ||
(board[0][2] != null && board[0][2] == board[1][1] && board[1][1] == board[2][0]);
}
boolean isValidMove(Point move, ArrayList<Point> availableMoves)
{
for(int i = 0; i<availableMoves.size(); i++)
{
Point curr = availableMoves.get(i);
if(move.x == curr.x && move.y == curr.y)
return true;
}
return false;
}
void moveForBot(Node node)
{
Node newNode = new Node();
newNode.board = node.board;
newNode.nextPlayer = "X";
newNode = nextMove(newNode);
printBoard(newNode);
System.out.println("Your turn, press enter to continue");
if(checkWin(newNode))
{
System.out.println("You lost! Press enter to play again");
}
else if(isLeafNode(newNode))
{
System.out.println("Draw Game! Press enter to play again");
}
else
{
scanner.nextLine();
moveForPlayer(newNode);
}
}
Node nextMove(Node node){
getMiniMaxAlphaBeta(node, getAlpha(node), getBeta(node));
Node newNode = getMaxNodeFromPossibleMoves();
possibleNextMoves.clear();
return newNode;
}
Node getMaxNodeFromPossibleMoves()
{
Node maxNode = possibleNextMoves.get(0);
for(int i = 0, l = possibleNextMoves.size(); i<l; i++)
{
if(maxNode.heuristicValue < possibleNextMoves.get(i).heuristicValue)
{
maxNode = possibleNextMoves.get(i);
}
}
return maxNode;
}
int getMiniMaxAlphaBeta(Node node, int alpha, int beta)
{
if(isLeafNode(node) || node.traversalDepth >= MAX_TRAVERSAL_DEPTH)
return miniMaxLeafNode(node);
else if(node.nextPlayer.equals("O"))
return minimaxAlphaBetaForMinimizer(node, alpha, beta);
else
return minimaxAlphaBetaForMaximizer(node, alpha, beta);
}
int minimaxAlphaBetaForMinimizer(Node node, int alpha, int beta)
{
ArrayList<Node> allSuccessors = getAllSuccessors(node);
for(int i = 0, l = allSuccessors.size(); i<l; i++)
{
Node s = allSuccessors.get(i);
int currMin = getMiniMaxAlphaBeta(s, alpha, beta);
beta = Math.min(beta, currMin);
node.heuristicValue = Math.min(node.heuristicValue, beta);
if(alpha >= beta)
break;
}
if(possibleNextMoves(node) != null)
possibleNextMoves.add(node);
return beta;
}
int minimaxAlphaBetaForMaximizer(Node node, int alpha, int beta)
{
ArrayList<Node> allSuccessors = getAllSuccessors(node);
for(int i = 0, l = allSuccessors.size(); i<l; i++)
{
Node s = allSuccessors.get(i);
int currMax = getMiniMaxAlphaBeta(s, alpha, beta);
alpha = Math.max(alpha, currMax);
node.heuristicValue = Math.max(node.heuristicValue, alpha);
if(alpha >= beta)
break;
}
if(possibleNextMoves(node) != null)
possibleNextMoves.add(node);
return alpha;
}
int getAlpha(Node node)
{
if(isLeafNode(node))
return evaluateHeuristicValue(node);
return -INFINITY;
}
int getBeta(Node node)
{
if(isLeafNode(node))
return evaluateHeuristicValue(node);
return INFINITY;
}
ArrayList<Node> possibleNextMoves = new ArrayList<Node>();
int miniMaxLeafNode(Node node)
{
if(possibleNextMoves(node) != null)
possibleNextMoves.add(node);
return evaluateHeuristicValue(node);
}
ArrayList<Node> getAllSuccessors(Node node)
{
ArrayList<Node> successors = new ArrayList<Node>();
ArrayList<Point> availableMoves = getAvailableMoves(node);
for(int i = 0, l = availableMoves.size(); i<l; i++)
{
successors.add(getSuccessor(node, availableMoves.get(i)));
}
return successors;
}
Node possibleNextMoves(Node node){
if(node.atDepth == 1)
return node;
else
return null;
}
ArrayList<Point> getAvailableMoves(Node node)
{
ArrayList<Point> availableMoves = new ArrayList<Point>();
for(int i = 0; i<3; i++)
{
for(int j = 0; j<3; j++)
{
if(node.board[i][j] == null)
{
availableMoves.add(new Point(j, i));
}
}
}
return availableMoves;
}
Node getSuccessor(Node node, Point p) {
if(isLeafNode(node)) return null;
return new Node(updateBoard(node, p), node, evaluateHeuristicValue(node), node.atDepth+1, node.nextPlayer.equals("X") ? "O" : "X", node.traversalDepth+1);
}
int evaluateHeuristicValue(Node node)
{
if(node.nextPlayer == "X" && this.checkWin(node)==true) return -1;
if(node.nextPlayer == "O" && this.checkWin(node)==true) return 1;
return 0;
}
String[][] updateBoard(Node node, Point p) {
String[][] newBoard = copyBoard(node.board);
newBoard[p.y][p.x] = node.nextPlayer;
return newBoard;
}
String[][] copyBoard(String[][] aBoard) {
int boardSize = aBoard.length;
String[][] newBoard = new String[boardSize][boardSize];
for(int row = 0; row < boardSize; row++) {
for(int column = 0; column < boardSize; column++) 
newBoard[row][column] = aBoard[row][column];
}
return newBoard;
}
void printBoard(Node node){
String board[][] = node.board;
clear();
System.out.println("==========================");
System.out.println("| Impossible Tic-Tac Toe |");
System.out.println("==========================");
System.out.println("-----------");
System.out.println("|y\\x| 0 | 1 | 2 |");
for(int i = 0; i<3; i++)
{
System.out.println("-----------");
System.out.print("| " + i + " ");
for(int j = 0; j<3; j++)
{
System.out.print("| ");
if(board[i][j] != null)
System.out.print(board[i][j]);
else
System.out.print(" ");
System.out.print(" ");
}
System.out.println("|");
}
System.out.println("-----------");
}
public static void main(String[] args) {
new Main();
}
}

Simple Android Chat Application Part 2

posted Nov 14, 2015, 2:12 AM by Kenrick Satrio Sahputra   [ updated Aug 15, 2016, 11:35 PM by Surya Wang ]


Okay, continue from the last tutorial, where we have already setup the GCM stuff, 
now it's time for us to send a downstream message.
  
Downstream message is a message that is sent from our server to GCM Server. 
After that, GCM will figure out where this message should be passed, using token id as it's identifier, then it will send the message to the target device 
But first, our chat application must have a service, that can listen to a new incoming GCM message. 
That's is the overview of how GCM downstream message will work.

But first of all, we need to build our application interface first. For the sake of simplicity, we will only have one View. 
That view will contain 2 input control, 1 button, and 1 list view. 
The first input control will be used as a sender's name, the other will be used as a message content.
The button will be used to send the message.
The list view will contain all chat conversation.

Pretty simple, so let's get started.

1. Change the view in R.layout.activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<EditText
android:id="@+id/username"
android:hint="Username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<Button
android:id="@+id/send_button"
android:text="Send Message"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<EditText
android:id="@+id/message_input"
android:layout_toLeftOf="@id/send_button"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<ListView
android:id="@+id/chat_list_container"
android:layout_below="@id/username"
android:layout_above="@id/send_button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</RelativeLayout>
2. Next modify our MainActivity class.
    -. Register a local broadcast receiver so we can handle a new incoming message.
    -. Register button click listener.
    -. Set the cookie storage, so our http request will have the same session id over time.
    -. Set the adapter for our list view, so it can display the chat conversation.
public class MainActivity extends AppCompatActivity {

private EditText mUsernameEt;

private EditText mMessageEt;

private View mSendButton;

private ListView mListView;

private ChatMessageAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new ChatMessageAdapter(this);

setContentView(R.layout.activity_main);

prepareView();

mSendButton.setOnClickListener(sendBtnClickListener);
mListView.setAdapter(mAdapter);

// Start a service to register GCM token id to server
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);

// register the local broadcast manager
// to receive message from GCM Listener
LocalBroadcastManager.getInstance(this).registerReceiver(
new ChatReceiver(),
new IntentFilter("message"));

// set the cookie storage, so our http request will have the same sessio id
CookieManager cookieManage = new CookieManager();
CookieHandler.setDefault(cookieManage);
}

private void prepareView() {
mUsernameEt = (EditText) findViewById(R.id.username);
mMessageEt = (EditText) findViewById(R.id.message_input);
mSendButton = findViewById(R.id.send_button);
mListView = (ListView) findViewById(R.id.chat_list_container);
}

private View.OnClickListener sendBtnClickListener = new View.OnClickListener() {

@Override
public void onClick(View v) {
String from = mUsernameEt.getText().toString();
String message = mMessageEt.getText().toString();

// Send a broadcast message
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
requestQueue.add(new StringRequest(
Request.Method.GET,
"http://192.168.1.120:8080/AndroidChatServer/broadcast?" +
"from=" + from + "&" +
"message=" + message, null, null));
requestQueue.start();

// Add our own chat to the list
Chat chat = new Chat("You", message, true);
mAdapter.addChatList(chat);

// Scroll the list view to the bottom
mListView.setSelection(mAdapter.getCount() - 1);

// reset input text field
mMessageEt.setText("");
}
};

public class ChatReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
Chat chat = new Chat(extras.getString("from"), extras.getString("message"), false);
mAdapter.addChatList(chat);

// Scroll the list view to the bottom
mListView.setSelection(mAdapter.getCount() - 1);
}
}

}

3. Create a Chat class, that will encapsulate our chat content
public class Chat {

private String from;

private String message;

// will determine if the message is our own message
private boolean isSelf;

public Chat(String from, String message, boolean isSelf) {
this.from = from;
this.message = message;
this.isSelf = isSelf;
}

public String getFrom() {
return from;
}

public String getMessage() {
return message;
}

public boolean isSelf() {
return isSelf;
}
}

4. Create a ChatMessageAdapter class, to display a list view item

public class ChatMessageAdapter extends BaseAdapter {

private List<Chat> chatList;

private Context context;

public ChatMessageAdapter(Context context) {
this.context = context;
this.chatList = new ArrayList<>();
}

@Override
public int getCount() {
return chatList.size();
}

@Override
public Chat getItem(int position) {
return chatList.get(position);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

if(null == convertView)
convertView = LayoutInflater.from(context).inflate(R.layout.list_chat_view, parent, false);

Chat chat = getItem(position);

TextView fromTv = (TextView) convertView.findViewById(R.id.chat_from_container);
TextView messageTv = (TextView) convertView.findViewById(R.id.chat_message_container);

// Change the alignment of our own message
if(chat.isSelf()) {
fromTv.setGravity(Gravity.END);
messageTv.setGravity(Gravity.END);
} else {
fromTv.setGravity(Gravity.START);
messageTv.setGravity(Gravity.START);
}

fromTv.setText(chat.getFrom());
messageTv.setText(chat.getMessage());

return convertView;
}

@Override
public long getItemId(int position) {
return 0;
}

public void addChatList(Chat chat) {
this.chatList.add(chat);
this.notifyDataSetChanged();
}

}
5. Register a GCM Listener, an object that will be notify, when a new gcm message arrived.

public class MyGCMListenerService extends GcmListenerService {

@Override
public void onMessageReceived(String from, Bundle data) {

Intent intent = new Intent("message");
intent.putExtra("from", data.getString("sender"));
intent.putExtra("message", data.getString("message"));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
And also don't forget to register the listener service in AndroidManifest.xml
<service
android:name=".MyGCMListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>

So that's it all the code we need for android. Next we will modify our web server so that it can handle Incoming message from Android, 
then it will broadcast to all connected device.

1. Modify register servlet, so that it will saved the tokenId for each session id. 
We will use this session, to guard the broadcast message process, so that it doesn't broadcast it self (sender device).

@WebServlet(urlPatterns = "/register")
public class RegisterTokenServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

String token = req.getParameter("token");

// Add token to application scope
Set<String> tokens = (Set<String>) req.getServletContext().getAttribute("tokens");
if(tokens == null) tokens = new HashSet<>();
tokens.add(token);
req.getServletContext().setAttribute("tokens", tokens);

// Add token to current session scope
req.getSession().setAttribute("token", token);

resp.getWriter().println("Success receive token: " + token);
}

}

2. Create a new BroadcastServlet to receive incoming message and broadcast to all connected device.

@WebServlet(urlPatterns = "/broadcast")
public class BroadcastChatServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

String currentToken = (String) req.getSession().getAttribute("token");

// Get all connected device's token id
Set<String> tokens = (Set<String>) req.getServletContext().getAttribute("tokens");

// Validate so that, if no one have register
// the application not yields an error
if(tokens == null) return;

tokens.forEach(token -> {

// No need to send GCM Message to our own self
if(token.equals(currentToken)) return;

ResteasyClient client = new ResteasyClientBuilder().build();
client.register((ClientRequestFilter) request -> {
request.getHeaders().add("Content-Type", "application/json");
request.getHeaders().add("Authorization", "key=AIzaSyBUwoBTcSHP7xvFrG3cx6mwJPhj9g84A3o");
});
ResteasyWebTarget target = client.target("https://gcm-http.googleapis.com/gcm/send");

Map<String, Object> body = new HashMap<>();
Map<String, String> data = new HashMap<>();

String from = req.getParameter("from");
if(from == null || "".equals(from))
from = "Anonymous";

data.put("sender", from);
data.put("message", req.getParameter("message"));

body.put("to", token);
body.put("data", data); target.request().post(Entity.json(body));

});
}
}

That's it, pretty simple. I hope you guys understand the bold point here, and you can development your own realtime application.


Swift, A Beginner’s Guide

posted Nov 1, 2015, 11:50 PM by Justinus Hermawan Sadeli   [ updated Aug 15, 2016, 11:38 PM by Surya Wang ]


Swift Logo

Swift is a new programming language for iOS, OS X, watchOS, and tvOS apps. Swift was introduced at Apple’s Worldwide Developer Conference (WWDC) 2014 as a replacement for Objective C, Apple’s long-time programming language. At WWDC 2015, Swift 2.0 was announced with a number of improvements and changes, including Apple’s commitment to make Swift an open source language.

In this tutorial, you will learn about the beginning of Swift programming, especially about how to make a Swift command-line app as the beginning to make a full iOS app with a beautiful user interface. This will provide you with the basic knowledge you’ll need to make the amazing apps you’ve dreamed of!

Getting Started

Note: The Swift version that I used in this tutorial is Swift 2.1 and the Xcode version is Xcode 7.

The very first step is to download Xcode (the software program that you write your apps in) for free on the Mac App Store.
SS 1

Be sure you have the latest version of Xcode. You need Xcode 6 or later to work with Swift. Once you have Xcode installed, open it and click the Get started with a playground option.

SS 2

Note: If you don’t see the Welcome to Xcode window, click Window -> Welcome to Xcode to display it.

A playground is a simple way to experiment and play around with Swift code. It allows you to edit the code listings and see the result immediately.

You can’t run a playground as an iPhone app, but it will be very important in helping you to understand the basic of Swift. Don’t worry, you’ll be creating your very own iPhone app soon enough!

Set the name to MyFirstPlayground, the platform to iOS, and click Next. Save the playground wherever you wish.

SS 3
Introduction to Playgrounds

You will see that the playground you created already has three lines of code:
 // Playground - noun: a place where people can play
 
 import UIKit
 
 var str = "Hello, playground"

Here’s the breakdown of the playground, one line at a time.

 // Playground - noun: a place where people can play

The first line, which starts with two forward slashes, is called a comment.

This line is only there for you, or any other programmers that look at your code, to see. It does not affect the way that your code functions. Think of it as “a way to write notes in your code”.

 import UIKit

The second line imports UIKit, which you can think of as “a bunch of code written by the smart folks at Apple.” All you need to know about this right now is that you need it for the rest of your code to work.

 var str = "Hello, playground"

The third line is the one you should focus on right now.

This line creates a variable named str that holds the value Hello, playground. On the right side of the window, you can see that Xcode is keeping track of what this variable holds.

SS 4

Try changing “Hello, playground” to “Hello, Swift!”, like this:

 var str = "Hello, Swift!"
SS 5

Did you see how the right side of the window changed what it said too? Congratulations, you just did some Swift programming!

Playground as a Calculator

Now let’s see what else you can do in playground. At the bottom of your file, try typing a basic math expression such as 9 % 5 (9 modulo by 5) and hit enter to go to the next line:

 9 % 5

The playground will give you the answer to your equation over in the shaded right side of the window:

SS 6

Cool, yeah? You can do any other extreme math operation too.

Challenge: Quick – try to use Playgrounds to show me the result of 1234 * 5678!

Using playground as a simple calculator is great and all, but now let’s move on and get into some more coding! Delete everything in the program except for import UIKit so you have a clean state to work with.

 import UIKit

It is extremely important that you leave that line there because the Playground will not work without it.

Variables

Next, it’s time for you to play with variables. You use variables to store values. When you create a variable, you always use the following syntax:

 var variableName: variableType = initialValue

Except you substitute the following:

·  variableName: Whatever you want to name the variable, like str or age for example.

·  variableType: The type of the variable, like String or Int. More on that in a moment.

·  initialValue: Whatever you want to set the initial value of the variable to, like “Hello, Swift!” or 22.

Try an example! Add these lines to the bottom of your playground:

 var str: String = "Hello, Swift!"
 var age: Int = 22

In the first line, you created a variable named str of type String, and set the initial value to “Hello, Swift!”

In the second line, you created a variable named age of type Int, and set the initial value to 22.

You’re starting to get a good idea how to make a variable, but you may be wondering what the difference between String and Int types is, and what other data types you can use.

Data Types

Here’s a list of some of the basic data types that Swift has to offer:

·  Int – whole numbers, or integers

·  Double – decimal numbers

·  Bool – a value that can be true, or false

·  String – a “string” of letters or words

Practice using these data types. Add the following lines to the bottom of your playground:

 var currentTime: Int = 12
 var costOfBurger: Double = 20.55
 var isHungry: Bool = true
 var name: String = "Kosur"

Notice how each of the values show up on the shaded right area of the window as the playground keeps track of them. Now, try changing the value of the String name.

Add the following line to the bottom of your playground:

 name = "Komala"

This line did not require the use of var or : String because the object was already created. All you did here was change the value that it was storing.

Challenge: Your turn to give it a try. At the bottom of the file, try creating a variable to represent your favorite movie.

Implementing Operation Invoker and Operation Behavior in WCF

posted Oct 15, 2015, 2:58 AM by Hadi Setiawan   [ updated Aug 15, 2016, 11:31 PM by Surya Wang ]


Operation invoker is responsible for invoking the service implementation of a service contract. By writing our own operation invoker, we can determine what will happen around or intercept the actual service implementation. For example, we can cache the response or log the request. In this tutorial we'll use operation invoker along with behaviors to log invokes to our service.

We'll create a simple service, the GreeterService, which will have one operation, Greet. Greet operation will take a name, and return a greeting to that person. And we'll log each invocation and the time it was invoked to the output window.

Getting Started

The IDE used in this tutorial is Microsoft Visual Studio 2015. And to test the service, we'll be using WCF Test Client, which should be bundled with installation of Visual Studio.

Creating the service

We'll create an empty web project and name it Tutorial. Let's first add assembly reference of System.ServiceModel. After that, we'll create our Greeter service contract and its implementation. We'll create them inside the Service namespace. We'll put the Service namespace inside the Service folder of our root directory.

Service\IGreeterService.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using System.ServiceModel;

namespace Tutorial.Service
{
    [ServiceContract]
    interface IGreeterService
    {
        [OperationContract]
        string Greet(string name);
    }
}
Service\GreeterService.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
namespace Tutorial.Service
{
    public class GreeterService : IGreeterService
    {
        public string Greet(string name)
        {
            return "Hello, " + name;
        }
    }
}

Now that the code for the service is done, we'll try to build our project. The purpose of building our project is so that we there will be autocomplete when we modify the web.config file. After building the project, we'll configure our project so it will activate the service without .svc files.

web.config
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<configuration>
  <!-- ... -->
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Tutorial.Service.GreeterService">
        <endpoint binding="basicHttpBinding" contract="Tutorial.Service.IGreeterService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <serviceHostingEnvironment>
      <serviceActivations>
        <add service="Tutorial.Service.GreeterService" relativeAddress="~/Greeter.svc" />
      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
  <!-- ... -->
</configuration>

After this configuration, the service should be accessible at http://localhost:port/Greeter.svc. Make sure that the service is activated by going to the hosted address on your local computer.

Creating the Operation Invoker

To create an operation invoker, the class need to implements the System.ServiceModel.Dispatcher.IOperationInvoker interface. This interface have 5 methods to be implemented. In this tutorial, we only care about the Invoke method. In this method, we'll log the service invocation.

Keep in mind that we won't be implementing the operation invoker fully. In fact, we'll still use the default invoker. So methods in the operation invoker we create, will be delegated to the default invoker eventually.

Invoker\LogOperationInvoker.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Diagnostics;
using System.ServiceModel.Dispatcher;

namespace Tutorial.Invoker
{
    public class LogOperationInvoker : IOperationInvoker
    {
        public IOperationInvoker DefaultInvoker { get; set; }

        public string OperationName { get; set; }

        public bool IsSynchronous
        {
            get
            {
                return DefaultInvoker.IsSynchronous;
            }
        }

        public object[] AllocateInputs()
        {
            return DefaultInvoker.AllocateInputs();
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            Debug.WriteLine("[{0:yyyy-MM-dd H:mm:ss}] {1}.{2}({3})",
                DateTime.Now, instance.GetType().FullName, OperationName, String.Join(", ", inputs));

            return DefaultInvoker.Invoke(instance, inputs, out outputs);
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            return DefaultInvoker.InvokeBegin(instance, inputs, callback, state);
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            return DefaultInvoker.InvokeEnd(instance, out outputs, result);
        }
    }
}

The code for LogOperationInvoker is done now. The important thing to notice here is that we'll delegate every method invocation to the default invoker, but the Invoke method, will log the request to the output before delegating to the default invoker. Note that if you want to create other implementation instead of logging, for example caching the response, you should do it in the Invoke method.

Gluing things together

Now that we have both our service and operation invoker ready, how do we use them both? As now when we invoke the greet operation, the request won't be logged and shown on output window. The glue to these two is the behavior. By using behavior, we'll change the default operation invoker of our GreeterService to LogOperationInvoker.

There're four types of behavior if categorized by their scope:

  • Service Behavior
  • Contract Behavior
  • Endpoint Behavior
  • Operation Behavior

We'll add these behavior to our service programmatically, by using attributes. But we can't apply endpoint behavior programmatically, as endpoint is configured inside the web.config file. That's not the case for contract, operation, and service, as we can access it from our code.

Here is where you should put the each attribute when you want to apply certain behavior. Contract behavior is at ServiceContract, operation behavior is at OperationContract, and service behavior is at the class that implements ServiceContract. For more detailed explanation about behavior and other mechanism to apply a behavior, please visit this link.

Behavior\LogOperationBehavior.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Tutorial.Invoker;

namespace Tutorial.Behavior
{
    public class LogOperationBehavior : Attribute, IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription,
            BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription,
            ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription,
            DispatchOperation dispatchOperation)
        {
            dispatchOperation.Invoker = new LogOperationInvoker
            {
                DefaultInvoker = dispatchOperation.Invoker,
                OperationName = operationDescription.Name
            };
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }
}
Behavior\LogContractBehavior.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace Tutorial.Behavior
{
    public class LogContractBehavior : Attribute, IContractBehavior
    {
        public void AddBindingParameters(ContractDescription contractDescription,
            ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ContractDescription contractDescription,
            ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ContractDescription contractDescription,
            ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
        {
            foreach (var operation in contractDescription.Operations)
            {
                if (!operation.OperationBehaviors.Contains(typeof(LogOperationBehavior)))
                {
                    operation.OperationBehaviors.Add(new LogOperationBehavior());
                }
            }
        }

        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
        }
    }
}
Behavior\LogServiceBehavior.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

namespace Tutorial.Behavior
{
    public class LogServiceBehavior : Attribute, IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase)
        {
            foreach (var endpoint in serviceDescription.Endpoints)
            {
                foreach (var operation in endpoint.Contract.Operations)
                {
                    if (!operation.OperationBehaviors.Contains(typeof(LogOperationBehavior)))
                    {
                        operation.OperationBehaviors.Add(new LogOperationBehavior());
                    }
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }
}

You can also create a class which will implements all three interface (IOperationBehavior, IContractBehavior, IServiceBehavior). I separated them into three different classes is for clarity reason.

All that's left to do is applying the behavior to our service. Because we have three types of different behavior, we'll also have three ways to use them.

The code below applies both the operation and contract behavior.

Service\IGreeterService.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
using System.ServiceModel;
using Tutorial.Behavior;

namespace Tutorial.Service
{
    [LogContractBehavior]
    [ServiceContract]
    interface IGreeterService
    {
        [LogOperationBehavior]
        [OperationContract]
        string Greet(string name);
    }
}

The code below applies the service behavior.

Service\GreeterService.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
using Tutorial.Behavior;

namespace Tutorial.Service
{
    [LogServiceBehavior]
    public class GreeterService : IGreeterService
    {
        public string Greet(string name)
        {
            return "Hello, " + name;
        }
    }
}

Even though I applied all three behavior to the same service, it does not mean that you should apply them all too. You should apply the behavior according to the scope you want. For example you just want to log one operation of a certain service, then you should just use the LogOperationBehavior on the said operation.

Let's try invoking our GreeterService and see the result in the output window. I'll be using WcfTestClient mentioned earlier. Below is the screenshot of both the test client and the output window.

WCF test client
WCF test client
Output window
Logged message in output window

Conclusion

By leveraging the extensibility of WCF, invoker and behavior, we can intercept any request at the service level. For further implementation, it's better that you combine the behavior into one class, as this simplifies a lot the usage of the behavior.

You can download the source code below.

Steganography using Pixel Value Differencing (PVD) Algorithm Implemented with Java (Part 2) - PVD Program

posted Jun 18, 2015, 1:02 AM by Sujono _   [ updated Aug 15, 2016, 11:38 PM by Surya Wang ]


Welcome back to the Steganography and PVD Tutorial. In this second and final part, we will implement the PVD algorithm using Java.

1.    The Code
       
       We will use Java for this tutorial. You can use any Java IDE, such as Eclipse, Netbeans, etc. It would not be a problem because we just need some basic features of Java.
  • Prelude.
       First of all, we need to declare all of the list that we need, Here are the list that we need:  
        I) vecRed, vecGreen, and vecBlue for RGB 
        II) vecDI for di 
        III) vecDI2 for di' 
        IV) vecTable for range bit that can be inserted into the current pixel.
        V) vecLJ for lj
        VI) vecUJ for uj
        VII) vecWJ for wj
        VIII) vecTI for ti
        IX) vecTI2 for ti'
        X) vecM for M
        XI) vecASCII for ASCII of all pixels
        XII) vecBinaryWord for the binary word from all character of the inserted message, and
        XIII) vecBinaryDecrypt for the binary word that get from the decrypt process.

  • Create the function to get the RGB value from the picture and save them in three different list or vector (vecRed, vecGreen, and vecBlue).
         
      void getPixelRGB(){
               try {
                vecRed.clear();
                vecGreen.clear();
                vecBlue.clear();
                img = ImageIO.read(new File(txtInputFile.getText()));
                width = img.getWidth();
                height = img.getHeight();
                pixels = img.getRGB(0,0, img.getWidth(), img.getHeight(), null, 0, img.getWidth());
                for(int i=0;i<pixels.length;i++)
                {
                    c = new Color(pixels[i]);
                    vecRed.add(c.getRed());
                    vecGreen.add(c.getGreen());
                    vecBlue.add(c.getBlue());
                }
            } catch (IOException ex) {
                Logger.getLogger(SteganoPVD.class.getName()).log(Level.SEVERE, null, ex);
            }
       }
  • Create the function to change the input text into ASCII binary with 8 length bits and save it into a vecASCII.
    void setBinaryText(){
        vecASCII.clear();
        String tmp = "";
        tmp = txtInputMsg.getText();
        for(int i=0;i<tmp.length();i++)
        {
          char temp = tmp.charAt(i);
          vecASCII.add((int)temp);
        }
        for(int i=0;i<vecASCII.size();i++)
        {
            String old_biner = Integer.toBinaryString(vecASCII.get(i));
            int min = 8 - old_biner.length();
            String new_biner = "";
            for(int j=0;j<min;j++)
            {
                new_biner+="0";
            }
            new_biner+=old_biner;
            vecBinaryWord.add(new_biner);
        }
       }
  • Create the function to reset all of the list except RGB list.
  void resetAll(){
      vecDI.clear();
      vecLJ.clear();
      vecWJ.clear();
      vecUJ.clear();
      vecTI.clear();
      vecTI2.clear();
      vecDI2.clear();
      vecM.clear();
    }
  • Create the function for the PVD Algorithm. We will change the red value of the vecRed and no need to change other RGB list. Each binary word will be inserted into four pixel where each pixel contain a quarter of one binary word.
  void PVDProcess(){
      colIndex = rowIndex = 0;
      for(int a=0;a<vecBinaryWord.size();a++)
      {
      for(int b=0;b<8;b+=2)
      {
        vecDI.add(Math.abs(vecRed.get(colIndex)-vecRed.get(colIndex+1)));
        low = high = cek = 0;
        for(int i=0;i<vecTable.size();i++)
        {
          high+=vecTable.get(i);
          --high;
          if(vecDI.get(vecDI.size()-1)>=low&&vecDI.get(vecDI.size()-1)<=high)
          {
              cek = i;
              vecLJ.add(low);
              vecUJ.add(high);
          }
          low = ++high;
         }
        vecWJ.add(vecUJ.get(vecUJ.size()-1)-vecLJ.get(vecLJ.size()-1)+1);
        vecTI.add(Math.log(vecWJ.get(vecWJ.size()-1))/Math.log(2));
        int sizeBit = vecTI.get(vecTI.size()-1).intValue();
        int angka = Integer.parseInt(vecBinaryWord.get(a).substring(b,b+sizeBit),sizeBit);
        vecTI2.add(angka);
        vecDI2.add(vecTI2.get(vecTI2.size()-1)+vecLJ.get(vecLJ.size()-1));
        vecM.add(Math.abs(vecDI2.get(vecDI2.size()-1)-vecDI.get(vecDI.size()-1)));
        if(vecRed.get(colIndex)>=vecRed.get(colIndex+1))
        {
          if(vecDI2.get(vecDI2.size()-1)>vecDI.get(vecDI.size()-1))
          {
              vecRed.set(colIndex, vecRed.get(colIndex)+(int)Math.ceil(vecM.get(vecM.size()-1)/2f));
              vecRed.set(colIndex+1, vecRed.get(colIndex+1)-(int)Math.floor(vecM.get(vecM.size()-1)/2f));
          }         
          else
          {
              vecRed.set(colIndex, vecRed.get(colIndex)-(int)Math.ceil(vecM.get(vecM.size()-1)/2f));
              vecRed.set(colIndex+1, vecRed.get(colIndex+1)+(int)Math.floor(vecM.get(vecM.size()-1)/2f));
          }
        }
        else
        {
          if(vecDI2.get(vecDI2.size()-1)>vecDI.get(vecDI.size()-1))
          {
              vecRed.set(colIndex, vecRed.get(colIndex)-(int)Math.floor(vecM.get(vecM.size()-1)/2f));
              vecRed.set(colIndex+1, vecRed.get(colIndex+1)+(int)Math.ceil(vecM.get(vecM.size()-1)/2f));
          }         
          else
          {
              vecRed.set(colIndex, vecRed.get(colIndex)+(int)Math.ceil(vecM.get(vecM.size()-1)/2f));
              vecRed.set(colIndex+1, vecRed.get(colIndex+1)-(int)Math.floor(vecM.get(vecM.size()-1)/2f));
          }
        }
        colIndex+=2;
      }
      }
   }
  • Create the function to call setBinaryText(), resetAll(), and PVDProcess()
    void setMessage(){
      setBinaryText();
      resetAll();
      PVDProcess();
      }
  • Create the function for show the normal and stego-image into jLabel.
    void setImage(){
        BufferedImage image = new BufferedImage(img.getWidth(),img.getHeight(), BufferedImage.TYPE_INT_RGB);
        WritableRaster raster = image.getRaster();
        for(int i=0;i<pixels.length;i++)
            pixels[i] = ((vecRed.get(i)&0x0ff)<<16)|((vecGreen.get(i)&0x0ff)<<8)|(vecBlue.get(i)&0x0ff);
        raster.setDataElements(0, 0, img.getWidth(),img.getHeight(), pixels);
        jLabel6.setIcon(new ImageIcon(image));
        try {
            img = ImageIO.read(new File(txtInputFile.getText()));
            jLabel1.setIcon(new ImageIcon(img));
        } catch (IOException ex) {
            Logger.getLogger(SteganoPVD.class.getName()).log(Level.SEVERE, null, ex);
        }
        jLabel1.setText("");
    }
  • Next is the display of the program. For this tutorial, we only need a simple interface. Here's my interface:

Picture 1. Display of The Program
  • We need the action event for each button. First, we need to create an event function for browse button. This function will show the file chooser dialog and fill the input based on the location of chosen file.
 
   private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
      choose = new JFileChooser();
      int rVal = choose.showOpenDialog(this);
      if (rVal == JFileChooser.APPROVE_OPTION) {
        txtInputFile.setText(choose.getCurrentDirectory().toString()+"\\"+choose.getSelectedFile().getName());
      }
      if (rVal == JFileChooser.CANCEL_OPTION) {
        txtInputFile.setText("");
      }
    }

  • Second, we create an event function to init the PVD process and show both image into jLabel.
   private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
        if(txtInputFile.getText().length()==0)
            JOptionPane.showMessageDialog(null,"Image must be chosen");
        else
        {
        for(int i=0;i<64;i++)
           vecTable.add(4);
        getPixelRGB();
        setMessage();
        setImage();
        jLabel5.setVisible(true);
        jLabel6.setVisible(true);
        jLabel1.setVisible(true);
        }
    }
  • Last, we create the function to regain our message from stego-image. 
    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {

        colIndex = rowIndex = 0;
        String new_biner = "";
        vecBinaryDecrypt.clear();
        resetAll();
        for(int i=0;i<txtInputMsg.getText().length()*8;i+=2)
        {
        vecDI.add(Math.abs(vecRed.get(i)-vecRed.get(i+1)));
        low = high = cek = 0;
        for(int j=0;j<vecTable.size();j++)
        {
          high+=vecTable.get(j);
          --high;
          if(vecDI.get(vecDI.size()-1)>=low&&vecDI.get(vecDI.size()-1)<=high)
          {
              cek = i;
              vecLJ.add(low);
              vecUJ.add(high);
          }
          low = ++high;
         }
        vecWJ.add(vecUJ.get(vecUJ.size()-1)-vecLJ.get(vecLJ.size()-1)+1);
        vecTI.add(Math.log(vecWJ.get(vecWJ.size()-1))/Math.log(2));
        vecDI2.add(vecDI.get(vecDI.size()-1)-vecLJ.get(vecLJ.size()-1));
        String old_biner = Integer.toBinaryString(vecDI2.get(vecDI2.size()-1));
        int min = 2 - old_biner.length();
        for(int j=0;j<min;j++)
        {
            new_biner+="0";
        }
        new_biner+=old_biner;
            if((i+2)%8==0&&i!=0)
            {
            vecBinaryDecrypt.add(new_biner);
            new_biner="";
            } 
            else if((i+2)>=txtInputMsg.getText().length()*8)
            {
            vecBinaryDecrypt.add(new_biner); 
            new_biner="";
            }
        }
        String Result="";
        for(int i=0;i<vecBinaryDecrypt.size();i++)
        {
            String tmp = Integer.toString(Integer.parseInt(vecBinaryDecrypt.get(i),2));
            char tmp2 = (char) Integer.parseInt(tmp);
            Result+=tmp2;
        }
       txtResultMsg.setText(Result);
    }

2.    Run The Program!
        
       We will use the lorem ipsum text for the embedded message. Here's my lorem ipsum text:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vulputate aliquet mauris, elementum elementum mi suscipit eu. Pellentesque consectetur mi mattis odio faucibus, non pellentesque sem sodales. Curabitur ultricies, nulla in ultricies cursus, purus quam posuere lorem, dapibus porta risus neque vitae velit. Nulla facilisi. Maecenas auctor quam et tempor bibendum. Nam eget mollis velit. Phasellus ultricies bibendum lacus at vulputate. Sed aliquet sem non massa porttitor, non vulputate massa aliquet. Fusce sit amet purus dapibus nisl tempus varius. Nunc in dapibus massa, sit amet vehicula augue. Nunc fermentum erat non metus mollis placerat.

Vivamus vitae odio iaculis, malesuada ex quis, volutpat velit. Mauris et placerat magna. Fusce eleifend ligula vel ultrices feugiat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus faucibus vel justo sodales dictum. Vivamus ut sodales diam, in volutpat justo. Sed luctus blandit euismod. Integer aliquet lorem lacinia, efficitur ex eget, ornare est.

Nam accumsan convallis sem a dapibus. Quisque placerat, massa nec finibus congue, ante leo volutpat enim, eu aliquam elit tortor posuere justo. Cras ultricies cursus mauris vel ullamcorper. Curabitur ut lorem et felis ullamcorper ultrices a et justo. Morbi aliquam odio enim, ut maximus ex vulputate dapibus. Sed gravida nisl eget pulvinar volutpat. Fusce vel vulputate dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

Nam sed enim et ante tincidunt aliquet vitae a nunc. Suspendisse pellentesque nec nisl sed viverra. Donec dui ligula, pellentesque interdum volutpat malesuada, fermentum a odio. Sed non dapibus libero. Curabitur tempus tempus purus. Vivamus porttitor placerat eros, id pretium ex. Curabitur mattis est arcu, in consequat odio dapibus quis. In mi nunc, finibus sed pellentesque in, accumsan eget ligula. Phasellus augue massa, gravida et interdum vel, semper a nisi. Nam eget elit sed tellus rhoncus placerat. Aliquam imperdiet dictum enim eu sollicitudin. Mauris leo massa, sodales sed porta a, venenatis egestas arcu. Vestibulum et tincidunt eros, eget fermentum nibh. Sed eget turpis mattis sem ornare luctus ac sollicitudin odio.

Etiam ut efficitur est. Nunc ut viverra est, in tristique nulla. Proin sit amet erat et risus efficitur gravida. Fusce eu volutpat urna. Etiam porttitor ultrices arcu et blandit. Etiam faucibus turpis nec finibus maximus. Aenean ullamcorper ipsum accumsan nunc pharetra dictum.


        Here's the result of both image after embedding process:


Picture 2. Image and Stego-Image Result

        Looks like there are no difference, right? It's because we cannot see the difference of pixels that is not too significant. As the test for performance of PVD algorithm, we will use Peak Signal-To-Noise Ratio (PSNR).

2.      Peak Signal Noise Rato (PSNR)

        The Peak Signal-To-Noise Ratio (PSNR) was utilized to evaluate the stego-image quality. The PSNR is defined as follows:


        Where:
        

is the pixel of the cover image where the coordinate is (i,j)

and 

is the pixel of the stego-image where the coordinate is (i,j)

        Good range values for the PSNR in image are between 30 and 50 dB, provided the bit depth is 8 bits, where higher is better. Here is the code:

    double round(double value, int places) {
        if (places < 0) throw new IllegalArgumentException();

        long factor = (long) Math.pow(10, places);
        value = value * factor;
        long tmp = Math.round(value);
        return (double) tmp / factor;
    }
    void PSNRCheck(){
       Double sum = new Double("0");
       for(int i=0;i<height;i++)
       {
           for(int j=0;j<width;j++)
           {
               int remain = vecOldRed.get((i*width)+j) - vecRed.get((i*width)+j);
               Double result = Math.pow((double)remain, 2);
               sum += result;
           }
       }
       Double MSE = sum/Double.parseDouble((width*height)+"");
       Double PSNR = 10 * Math.log10(Math.pow(255, 2)/MSE);
       JOptionPane.showMessageDialog(null, "Result : "+round(PSNR,2)+"db");
   }

Here is our PNSR using the lorem ipsum text:


Picture 3. PSNR Result

3.    The End

       We can conclude that PVD algorithm return a good result, but it's still need more improvement and modification to increase its performance. There are a lot of journal and paper that use its own method to modfy PVD algorithm. If you are interested, you also can learn many Steganography method beside PVD algorithm and combine them to get a better result.

      If you want to see and learn my code, I've also attached it in the attachment link below. Feel free to download!

      As an afterword, I would like to thank you for the readers and all person who have helped me in this tutorial. Hopefully this tutorial can help you to get more knowledge about Data security especially Steganography.  

     Learning source : http://citeseerx.ist.psu.edu/viewdoc/download?rep=rep1&type=pdf&doi=10.1.1.98.5855

1-10 of 65