import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JWindow;


/**
 * 
 */

/**
 * @author zeil
 *
 */
public class FunHouse_zeil {
	
	public class Point extends java.awt.Point {
		public Point (int x, int y) {
			super(x,y);
		}
		
		public String toString() {
			return "(" + x + "," + y + ")";
		}
		
		public boolean comesBefore (Point p)
		{
			return x < p.x || (x == p.x && y < p.y);
		}
	}

	// A graph mapping each point to the walls incident upon it.
	// This will be stored as a directed graph, so for each
	// wall segment of type T running between p1 and p2, house.get(p1)
	// will contain a Wall(p1,p2,T) and house.get(p2) will contain
	// a Wall(p2,p1,T).
	HashMap<Point, HashSet<Wall>> house;
	
	ArrayList<Room> rooms;

	static int wallCounter = 0;

	public class Wall {
		public Point p1;
		public Point p2;
		char wallType;
		int id;
		
		public Wall (Point p1, Point p2, char wtype) {
			this.p1 = p1;
			this.p2 = p2;
			wallType = wtype;
			if (wallType != 'W') {
				++wallCounter;
				id = wallCounter;
			} else
				id = 0;
		}
		
		Wall normalize() {
			if (p1.comesBefore(p2)) {
				return this;
			} else {
				return flip();
			}
		}
		
		
		Wall flip()
		{
			return new Wall(p2, p1, wallType);
		}
		
		
		public String toString() {return "" + id + " [" + p1.toString() + "," + p2.toString() + "]" + wallType;}
		
		@Override
		public int hashCode() {
			return p1.x + 13 * p1.y + 23 * p2.x + 17  * p2.y;
		}
		
		public boolean equals (Object obj) {
			Wall w = (Wall)obj;
			return w.wallType == wallType 
					&& w.p1.equals(p1)
					&& w.p2.equals(p2);
		}
	}
	
	
	public class Path extends ArrayList<Point> {
		
		public boolean equals(Object obj)
		{
			Path p = (Path)obj;
			if (p.size() != size())
				return false;
			for (int i = 0; i < size(); ++i)
				if (!get(i).equals(p.get(i)))
					return false;
			return true;
		}
		
	}
	
	class Room {
		public Path perimeter;
		public double area;
		public ArrayList<Wall> connectors;
		public int roomNumber;
		
		public Room (Path p)
		{
			perimeter = p;
			long sum = 0L;
			connectors = new ArrayList<Wall>();
			for (int i = 1; i < perimeter.size(); ++i) {
				Point p0 = perimeter.get(i-1);
				Point p1 = perimeter.get(i);
				sum += (long)p0.y * (long)p1.x - (long)p1.y*(long)p0.x;
				
				for (Wall w: house.get(p0)) {
					if (w.p2.equals(p1)) {
						if (w.wallType != 'W') 
							connectors.add(w);
						break;
					}
				}
			}
			area = Math.abs(((double)sum)/2.0);
		}
	}		
	
	static boolean DEBUG = false;
	
	public FunHouse_zeil()
	{
		house = new HashMap<Point, HashSet<Wall>>();
	}
	
	final static void run (BufferedReader bin)
	{
		Scanner in = new Scanner(bin);
		int n = in.nextInt();
		while (n > 0)
		{
			processDataSet(in, n);
			n = in.nextInt();
		}
	}

	private static void processDataSet(Scanner in, int n) {
		FunHouse_zeil funhouse = new FunHouse_zeil();
		for (int i = 0; i < n; ++i) {
			int x1, y1, x2, y2;
			char wallType;
			x1 = in.nextInt();
			y1 = in.nextInt();
			x2 = in.nextInt();
			y2 = in.nextInt();
			wallType = in.next().charAt(0);
			funhouse.addWallSegment(x1, y1, x2, y2, wallType);
			
		}
		
		funhouse.findRooms();
		funhouse.buildGraph();
		System.out.format ("%.1f%n", funhouse.maxFlow());
	}

	private double maxFlow() {
		while (true) {
			LinkedList<FlowgraphEdge> path = new LinkedList<FlowgraphEdge>();
			findUnderUtilizedPath(path);
			if (path.size() == 0)
				break;
			if (DEBUG) {
				System.err.println ("Max flow: looking at path " + path);
			}
			double increment = computeFlowIncrement(path);
			if (DEBUG) {
				System.err.println ("Max flow: increment is " + increment);
			}
			applyFlowIncrement (path, increment);
		}
		double sum = 0.0;
		for (FlowgraphEdge edge: flowGraph.get(source)) {
			sum += edge.flow;
		}
		return Math.abs(sum);
	}




	private void applyFlowIncrement(LinkedList<FlowgraphEdge> path,
			double increment) {
		for (FlowgraphEdge edge: path) {
	    	edge.flow += increment;
	    	edge.reverse.flow -= increment;
		}
	}

	private double computeFlowIncrement(LinkedList<FlowgraphEdge> path) {
		double increment = MAXFLOW;
		for (FlowgraphEdge edge: path) {
	    	double excess = 0.0;
	    	excess = edge.capacity - edge.flow;
	    	increment = Math.min(increment, excess);
		}
		return increment;
	}

	private void findUnderUtilizedPath(LinkedList<FlowgraphEdge> path)
	{
		path.clear();
		HashMap<Vertex,FlowgraphEdge> parentOf = new HashMap<Vertex,FlowgraphEdge>();
		parentOf.put(source, new FlowgraphEdge(source,  source, 0.0));
		LinkedList<Vertex> q = new LinkedList<Vertex>();
		q.add(source);
		while (q.size() > 0) {
			Vertex w = q.getFirst();
			q.removeFirst();
		    for (FlowgraphEdge edge: flowGraph.get(w)) {
		    	Vertex next = edge.to;
		    	if (parentOf.get(next) == null) {
		    		double excess = edge.capacity - edge.flow;
		    		if (excess > 0.0) {
		    			parentOf.put(next, edge);
		    			if (!next.equals(sink)) {
		    				q.add(next);
		    			} else {
		    				Vertex w0 = sink;
		    				while (!w0.equals(source)) {
		    					FlowgraphEdge e = parentOf.get(w0);
		    					path.addFirst(e);
		    					w0 = e.from.equals(w0) ? e.to : e.from;
		    				}
		    				return;	
		    			}
		    		}
		    	}
		    }
		}
	}


	public class Vertex {
		int roomNumber;
		char inOrOut;
		
		Vertex (int room, char entryExit) {
			roomNumber = room;
			inOrOut = entryExit;
		}
		
		public boolean equals( Object ov)
		{
			Vertex v = (Vertex)ov;
			return v.roomNumber == roomNumber && v.inOrOut == inOrOut;
		}
		
		public int hashCode() {
			return 2 * roomNumber + ((inOrOut == 'i')? 0 : 1);
		}
		
		public String toString() {
			return "" + roomNumber + inOrOut;
		}
	}

	public class FlowgraphEdge {
		Vertex from;
		Vertex to;
		double capacity;
		double flow;
		FlowgraphEdge reverse;
		
		public FlowgraphEdge(Vertex v1, Vertex v2, double cap) {
			this.from = v1;
			this.to = v2;
			capacity = cap;
			flow = 0.0;
			if (DEBUG) System.err.println ("Flowgraph edge" + this);
		}
		
		public String toString() {
			return from.toString() + "=>" + to + ": " + flow + "/" + capacity;
		}
	}
	
	HashMap<Vertex, LinkedList<FlowgraphEdge>> flowGraph;
	Vertex source;
	Vertex sink;
	static final double MAXFLOW = 1000.0 * 1000.0 + 1.0;
	
	private void buildGraph() {
		// Build a flowgraph for the funhouse
		//
		// For each room R, a pair of vertices Ri and Ro, an edge from Ri to Ro of capacity equal
		// to the room area. For each room Q connected to R by one or more doors, and edges with
		// capacity MAX from Qo to Ri and from Ro to Qi 
		// 
		flowGraph = new HashMap<Vertex, LinkedList<FlowgraphEdge>>();
		source = new Vertex(0, 'o');
		sink = new Vertex(0, 'i');
		flowGraph.put (source, new LinkedList<FlowgraphEdge>());
		flowGraph.put (sink, new LinkedList<FlowgraphEdge>());
		
		for (Room room: rooms) {
			Vertex ri = new Vertex(room.roomNumber, 'i');
			flowGraph.put (ri, new LinkedList<FlowgraphEdge>());
			Vertex ro = new Vertex(room.roomNumber, 'o');
			flowGraph.put (ro, new LinkedList<FlowgraphEdge>());
			FlowgraphEdge e = new FlowgraphEdge(ri, ro, room.area);
			FlowgraphEdge er = new FlowgraphEdge(ro, ri, 0);
			e.reverse = er;
			er.reverse = e;
			flowGraph.get(ri).add(e);
			flowGraph.get(ro).add(er);
		}			
		
		for (int i1 = 0; i1 < rooms.size(); ++i1) {
			Room room = rooms.get(i1);
			Vertex ri = new Vertex(room.roomNumber, 'i');
			Vertex ro = new Vertex(room.roomNumber, 'o');
			LinkedList<FlowgraphEdge> outgoing = flowGraph.get(ro);
			LinkedList<FlowgraphEdge> incoming = flowGraph.get(ri);
			for (int i2 = i1+1; i2 < rooms.size(); ++i2) {
				Room room2 = rooms.get(i2);
				boolean joined = false;
				for (int i = 0; i < room.connectors.size() && !joined; ++i) {
					Wall door1 = room.connectors.get(i).normalize();
					for (int j = 0; j < room2.connectors.size() && !joined; ++j) {
						Wall door2 = room2.connectors.get(j).normalize();
						joined = door1.equals(door2);
					}
				}
				if (joined) {
					Vertex r2i = new Vertex(room2.roomNumber, 'i');
					FlowgraphEdge e = new FlowgraphEdge(ro, r2i, MAXFLOW);
					FlowgraphEdge er = new FlowgraphEdge(r2i, ro, 0);
					e.reverse = er;
					er.reverse = e;
					outgoing.add(e);
					flowGraph.get(r2i).add(er);
					Vertex r2o = new Vertex(room2.roomNumber, 'o');
					e = new FlowgraphEdge(ri, r2o, 0);
					er = new FlowgraphEdge(r2o, ri, MAXFLOW);
					e.reverse = er;
					er.reverse = e;
					incoming.add(e);
					flowGraph.get(r2o).add(er);
				}
			}
			
			for (int i = 0; i < room.connectors.size(); ++i) {
				Wall door1 = room.connectors.get(i).normalize();
				if (door1.wallType == 'E') {
					// Entry - connect to source
					FlowgraphEdge e = new FlowgraphEdge(source, ri, room.area);
					FlowgraphEdge er = new FlowgraphEdge(ri, source, 0);
					e.reverse = er;
					er.reverse = e;
					flowGraph.get(source).add(e);
					incoming.add(er);
				} else if (door1.wallType == 'X') {
					// Exit - connect to sink
					FlowgraphEdge e = new FlowgraphEdge(ro, sink, room.area);
					FlowgraphEdge er = new FlowgraphEdge(sink, ro, 0);
					e.reverse = er;
					er.reverse = e;
					flowGraph.get(sink).add(er);
					outgoing.add(e);
				} 
			}
		}
	}

	// Set to true to plot the input data set - because getting the
	//    input correct is often actually quite tricky.
	static final boolean DISPLAY = true;
	static int zoom = 256;
	JScrollPane scrolledGraph;
	JPanel graph;
	private void findRooms() {
		if (DISPLAY) {
			JWindow win = null;
			JDialog dialog = new JDialog(win,"data set", Dialog.ModalityType.DOCUMENT_MODAL);
			dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			Container content = dialog.getContentPane();
			graph = new JPanel() {
				@Override
				public void paint (Graphics g) {
					super.paint(g);
					Graphics2D g2 = (Graphics2D) g;
	                g2.setStroke(new BasicStroke(2));
					for (Point p: house.keySet()) {
						for (Wall w: house.get(p)) {
							if (w.p1.x < w.p2.x || (w.p1.x == w.p2.x && w.p1.y < w.p2.y)) {
								Color c = Color.black;
								if (w.wallType == 'D')
									c = Color.yellow;
								else if (w.wallType == 'E')
									c = Color.green;
								else if (w.wallType == 'X')
									c = Color.red;
								g2.setColor(c);
								g2.drawLine(5 + zoom*w.p1.x/256, 5 + zoom*w.p1.y/256, 5 + zoom*w.p2.x/256, 5 + zoom*w.p2.y/256);
							}
						}
					}
				}
			};
			graph.setBackground(Color.white);
			graph.setPreferredSize(new Dimension(10 + 1000*zoom/256, 10 + 1000*zoom/256));
			content.setLayout(new BorderLayout());
			scrolledGraph = new JScrollPane(graph, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
			content.add (scrolledGraph, BorderLayout.CENTER);
			JPanel zoomPanel = new JPanel();
			content.add (zoomPanel, BorderLayout.SOUTH);
			JButton zoomIn = new JButton ("Zoom in");
			JButton zoomOut = new JButton ("Zoom out");
			zoomIn.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent e) {
					zoom = 2 * zoom;
					graph.setPreferredSize(new Dimension(10 + 1000*zoom/256, 10 + 1000*zoom/256));
					graph.revalidate();
					graph.repaint();
				}
			});
			zoomOut.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent e) {
					zoom = Math.max(1, zoom / 2);
					graph.setPreferredSize(new Dimension(10 + 1000*zoom/256, 10 + 1000*zoom/256));
					graph.revalidate();
					graph.repaint();
				}
			});
			zoomPanel.add (zoomIn);
			zoomPanel.add (zoomOut);
			dialog.setMinimumSize(new Dimension(500, 500));
			dialog.setVisible(true);
		}
		
		
		LinkedList<Path> cycles = findChordlessCycles();
		// There will be one inappropriate cycle around the perimeter of the house
		// We can recognize this because it will have the max area of all cycles;
		double maxArea = -1.0;
		int largest = -1;
		rooms = new ArrayList<Room>();
		for (Path p: cycles) {
			Room r = new Room(p);
			if (r.area > maxArea) {
				maxArea = r.area;
				largest = rooms.size();
			}
			rooms.add(r);
			r.roomNumber = rooms.size();
		}
		rooms.remove(largest);
	}

	private void addWallSegment(int x1, int y1, int x2, int y2, char wallType) {
		Point p1 = new Point(x1, y1);
		Point p2 = new Point(x2, y2);
		Wall w = new Wall(p1, p2, wallType);
		HashSet<Wall> outgoing = house.get(p1);
		if (outgoing == null) {
			outgoing = new HashSet<Wall>();
			house.put(p1, outgoing);
		}
		outgoing.add(w);
		outgoing = house.get(p2);
		if (outgoing == null) {
			outgoing = new HashSet<Wall>();
			house.put(p2, outgoing);
		}
		outgoing.add(w.flip());
	}

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main (String[] argv) throws IOException
	{
		BufferedReader in;
		if (argv.length > 0)
		{
			in = new BufferedReader(new FileReader(argv[0]));
		}
		else {
			in = new BufferedReader (new FileReader("funhouse.in"));
		}
		run (in);
	}

	// Find all of the chordless cycles (cycles that do not contain interior cycles)
	// in the house graph
	LinkedList<Path> findChordlessCycles() {
		// Copy the house graph so we can safely modify the copy 
		HashMap<Point, HashSet<Wall>> edges = new HashMap<Point, HashSet<Wall>>();
		for (Point pp: house.keySet()) {
			edges.put(pp,  new HashSet<Wall>(house.get(pp)));
		}
		
		LinkedList<Path> cycles = new LinkedList<Path>();
		boolean foundOne = true;
		while (foundOne) {
			foundOne = false;
			// Choose an arbitrary edge as a starting point
			for (Point pp: edges.keySet()) {
				HashSet<Wall> outgoing = edges.get(pp);
				if (outgoing != null && outgoing.size() > 0) {
					Wall w = outgoing.iterator().next();
					foundOne = true;
					Path p = new Path();
					p.add (w.p1);
					p.add (w.p2);
					edges.get(w.p1).remove(w);
					traceACycle (p, edges);
					if (DEBUG) System.err.println("Found cycle " + p);
					cycles.add(p);
					break;
				}
			}
		}
		return cycles;
	}

	
	/**
	 * Trace a cycle through a planar graph, removing edges as you go, by always
	 * taking the rightmost "turn" at each vertex.
	 * 
	 * @param cycle On output, contains the discovered cycle On input, contains the starting
	 * edge.
	 * 
	 * @param graph the graph being traversed
	 */
	private void traceACycle(Path cycle, HashMap<Point, HashSet<Wall>> graph) {
		Point p0 = cycle.get(0);
		Point p1 = cycle.get(1);
		while (!p0.equals(cycle.get(cycle.size()-1))) {
			p1 =  cycle.get(cycle.size()-2);
			Point p2 = cycle.get(cycle.size()-1);
			double lastAngle = Math.atan2(p2.y-p1.y, p2.x-p1.x);
			double smallestAngle = 3.0 * Math.PI;
			Point p3 = null;
			char p3Type = ' ';
			HashSet<Wall> outgoing = graph.get(p2);
			for (Wall w: outgoing) {
				if (!w.p2.equals(p1)) {
					double angle = Math.atan2(w.p2.y-p2.y, w.p2.x-p2.x);
					//System.err.print ("" + p1 + "->" + p2 + " " + lastAngle);
					//System.err.println ("    " + p2 + "->" + w.p2 + " " + angle);
					double c = (Math.PI + lastAngle) - angle;
					while (c > 2.0*Math.PI)
						c -= 2.0*Math.PI;
					while (c < 0.0)
						c += 2.0*Math.PI;
					if (c < smallestAngle) {
						smallestAngle = c;
						p3 = w.p2;
						p3Type = w.wallType;
					}
				}
			}
			cycle.add(p3);
			outgoing.remove(new Wall(p2, p3, p3Type));
			if (outgoing.size() == 0)
				graph.remove(p2);
		}
	}
	
	
	

}
